1 /*
2 * FreeWnn is a network-extensible Kana-to-Kanji conversion system.
3 * This file is part of FreeWnn.
4 *
5 * Copyright Kyoto University Research Institute for Mathematical Sciences
6 * 1987, 1988, 1989, 1990, 1991, 1992
7 * Copyright OMRON Corporation. 1987, 1988, 1989, 1990, 1991, 1992, 1999
8 * Copyright ASTEC, Inc. 1987, 1988, 1989, 1990, 1991, 1992
9 * Copyright FreeWnn Project 1999, 2000, 2002
10 *
11 * Maintainer: FreeWnn Project <freewnn@tomo.gr.jp>
12 *
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or
16 * (at your option) any later version.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 */
27
28 /* Copyright 1993 NEC Corporation, Tokyo, Japan.
29 *
30 * Permission to use, copy, modify, distribute and sell this software
31 * and its documentation for any purpose is hereby granted without
32 * fee, provided that the above copyright notice appear in all copies
33 * and that both that copyright notice and this permission notice
34 * appear in supporting documentation, and that the name of NEC
35 * Corporation not be used in advertising or publicity pertaining to
36 * distribution of the software without specific, written prior
37 * permission. NEC Corporation makes no representations about the
38 * suitability of this software for any purpose. It is provided "as
39 * is" without express or implied warranty.
40 *
41 * NEC CORPORATION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
42 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
43 * NO EVENT SHALL NEC CORPORATION BE LIABLE FOR ANY SPECIAL, INDIRECT OR
44 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
45 * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
46 * OTHER TORTUOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
47 * PERFORMANCE OF THIS SOFTWARE.
48 */
49
50 #ifndef lint
51 static char rcsid[] = "$Id: canna.c,v 1.9 2003/09/17 08:50:52 aida_s Exp $";
52 #endif
53
54 #include "commonhd.h"
55 #include "sdefine.h"
56 #include "sheader.h"
57 #include "wnn_config.h"
58 #if defined(__STDC__) || defined(__cplusplus)
59 # define pro(x) x
60 #else
61 # define pro(x) ()
62 #endif
63
64 #include <errno.h>
65
66 #if 0
67 /* old wchar(this also must work unless !SUPPORT_OLD_WCHAR) */
68 #ifndef _WCHAR_T
69 #define _WCHAR_T
70 #define _WCHAR_T_NOTDEFINED
71 #endif
72 #define wchar_t w_char
73 #include <canna/jrkanji.h>
74 #include <canna/RK.h>
75 #undef wchar_t
76 #ifdef _WCHAR_T_NOTDEFINED
77 #undef _WCHAR_T_NOTDEFINED
78 #undef _WCHAR_T
79 #endif
80 #else
81 #define CANNA_WCHAR16
82 #define CANNA_NEW_WCHAR_AWARE
83 #include <canna/jrkanji.h>
84 #include <canna/RK.h>
85 #endif
86
87 #include <fcntl.h>
88 #include <ctype.h>
89
90 #include "wnn_os.h"
91
92 #define MAXSIZE 1024
93 #define FULLREDRAW 2
94 #define PARTIALREDRAW 1
95 #define NOREDRAW 0
96
97 extern int errno;
98
99 static int maxmodelen;
100 static int maxwidth = 0;
101
102 static struct linebuf {
103 w_char line[MAXSIZE];
104 int length;
105 int revPos;
106 int revLen;
107 int cursorPos;
108 int displayLeft, displayRight;
109 int unchangedLeft, unchangedRight;
110 w_char mode_string[MAXSIZE];
111 int mode_string_length;
112 int mode_string_width;
113 } lbuf[2];
114
115 #define PREV 0
116 #define CRNT 1
117
118 static int curlbuf = 0;
119 #define prevlbuf (curlbuf ? 0 : 1)
120
121 static w_char leftover[2] = {(w_char)'<', (w_char)0};
122 static w_char rightover[2] = {(w_char)'>', (w_char)0};
123 static w_char lrok[2] = {(w_char)' ', (w_char)0};
124
125 static unsigned char buf[MAXSIZE * 2];
126
127 char *terminalname;
128
129 #ifdef NODEBUG
130 #define debug(fmt, a, b, c)
131 #else
132 #define debug debugprint
133 #endif
134
135
136 /*
137
138 The following three functions are defined in "w_string.c" in the
139 original uum source file.
140
141 Canna rewrote these functions.
142
143 w_char *Strncpy();
144 int eu_columlen();
145
146 */
147
148 w_char *
Strncpy(ws1,ws2,cnt)149 Strncpy(ws1, ws2, cnt)
150 w_char *ws1, *ws2;
151 int cnt;
152 {
153 w_char *ws;
154
155 if (ws2 == (w_char *)0)
156 return((w_char *)0);
157 if (ws2 < ws1 && ws1 < ws2 + cnt) {
158 while (cnt--) {
159 ws1[cnt] = ws2[cnt];
160 }
161 }
162 else {
163 int i = 0;
164 ws = ws1;
165 while (i++ < cnt && *ws2) {
166 *ws++ = *ws2++;
167 }
168 }
169 return ws1;
170 }
171
172 /*
173 eu_columlen counts the string width by `column'.
174 The following definition is very Japanese dependent.
175 */
176
177 int
eu_columlen(c)178 eu_columlen(c)
179 unsigned char *c;
180 {
181 int len = 0;
182 unsigned char ch;
183
184 while (ch = *c) {
185 if (ch & 0x80) {
186 if (ch == 0x8e) {
187 c++; len++; /* kana with half column width */
188 }
189 else if (ch == 0x8f) {
190 c += 3; len += 2; /* G3 kanji character */
191 }
192 else {
193 c += 2; len += 2; /* G1 kanji character */
194 }
195 }
196 else {
197 c++; len++; /* ascii alphabet */
198 }
199 }
200 return(len);
201 }
202
203 /*
204
205 The following functions are added for Canna.
206
207 In fact these functions are brought from Canna/lib/canna/util.c.
208
209 */
210
211 int
WStrlen(ws)212 WStrlen(ws)
213 w_char *ws;
214 {
215 int res = 0;
216 while (*ws++) {
217 res++;
218 }
219 return res;
220 }
221
222 int
WStrcmp(w1,w2)223 WStrcmp(w1, w2)
224 w_char *w1, *w2;
225 {
226 for (; *w1 && *w1 == *w2; w1++, w2++);
227 return(*w1 - *w2);
228 }
229
230 w_char *
WStrcpy(ws1,ws2)231 WStrcpy(ws1, ws2)
232 w_char *ws1, *ws2;
233 {
234 w_char *ws;
235 int cnt, len;
236
237 for (ws = ws2, cnt = 0 ; *ws ; ws++, cnt++) ;
238 len = cnt;
239 if (ws2 < ws1 && ws1 < ws2 + cnt) {
240 while (cnt--) {
241 ws1[cnt] = ws2[cnt];
242 }
243 }
244 else {
245 ws = ws1;
246 while (*ws2) {
247 *ws++ = *ws2++;
248 }
249 }
250 ws1[len] = (w_char)0;
251 return ws1;
252 }
253
254 static
colwidth(s,len)255 colwidth(s, len)
256 w_char *s;
257 int len;
258 {
259 int ret = 0;
260 w_char *es = s + len;
261
262 for (; s < es ; s++) {
263 switch (*s & 0x8080) {
264 case 0:
265 case 0x80:
266 ret ++;
267 break;
268 case 0x8000:
269 case 0x8080:
270 ret += 2;
271 break;
272 }
273 }
274 return ret;
275 }
276
277 /*
278
279 skipchar -- To skip characters until specified column width is
280 exhaused.
281
282 skipchar returns the number of skipped characters. Sometimes this
283 function over-run the column width. The amount to be over-run
284 returns in the argument ov.
285
286 This function is very Japanese dependent.
287
288 */
289
290 static
skipchars(s,wi,ov)291 skipchars(s, wi, ov)
292 w_char *s;
293 int wi, *ov;
294 {
295 int ret, swi;
296
297 for (swi = 0, ret = 0 ; swi < wi && s[ret] ; ret++) {
298 switch (s[ret] & 0x8080) {
299 case 0:
300 case 0x80:
301 swi ++;
302 break;
303 case 0x8000:
304 case 0x8080:
305 swi += 2;
306 break;
307 }
308 }
309 *ov = swi - wi;
310 return ret;
311 }
312
313
314 void
set_screen_vars_default()315 set_screen_vars_default() /* originally defined in basic_op.c */
316 {
317 maxwidth =
318 maxlength - maxmodelen - 2 + (conv_lines - 1) * (maxlength - 1) - 1;
319 }
320
321 /* canna routines */
322
init_uum()323 int init_uum() /* originally defined in prologue.c */
324 {
325 char **msg, *p;
326 extern char *prog;
327 extern void ring_bell();
328 extern (*jrBeepFunc) pro((void));
329 void registerkeys(), cannakeydef();
330
331 for (p = prog ; *p ; p++) { /* use basename */
332 if (*p == '/' && *(p + 1)) {
333 prog = p + 1;
334 }
335 }
336
337 if (def_servername[0]) {
338 wcKanjiControl(0, KC_SETSERVERNAME, def_servername);
339 }
340
341 if (defined_by_option & OPT_WNNKEY) {
342 wcKanjiControl(0, KC_SETINITFILENAME, uumkey_name_in_uumrc);
343 }
344 wcKanjiControl(0, KC_KEYCONVCALLBACK, (char *)cannakeydef);
345
346 wcKanjiControl(0, KC_INITIALIZE, (char *)&msg);
347 registerkeys();
348 jrBeepFunc = (int (*)())ring_bell;
349 if (msg) {
350 for (; *msg; msg++) {
351 puteustring(*msg, stdout);
352 puteustring("\r\n", stdout);
353 }
354 }
355 #ifdef KC_SETAPPNAME
356 wcKanjiControl(0, KC_SETAPPNAME, prog);
357 #endif
358 maxmodelen = wcKanjiControl(0, KC_QUERYMAXMODESTR, 0);
359
360 if (maxmodelen > MAXSIZE - 1) {
361 maxmodelen = MAXSIZE - 1;
362 }
363
364 set_screen_vars_default(); /* will set maxwidth */
365
366 wcKanjiControl(0, KC_SETWIDTH, (char *)(maxwidth + 1));
367 /* plus 1 is for ``rightover'' character. */
368
369 throw_cur_raw(0 ,crow + conv_lines);
370 if (keypad_fun) set_keypad_on();
371 scroll_up();
372 set_scroll_region(0 , crow - 1);
373 throw_cur_raw(0 ,crow - 1);
374 flush();
375
376 return 0; /* succeeded */
377 }
378
379 /*
380
381 The following function epilogue_no_close is copied from the original
382 uum source file epilogue.c, and a little bit modified for canna.
383
384 */
385
386 static struct RkRxDic *eseqdic; /* used at keyin1 */
387
epilogue_no_close()388 void epilogue_no_close() /* originally defined in epilogue.c */
389 {
390 wcKanjiControl(0, KC_FINALIZE, 0);
391 RkCloseRoma(eseqdic);
392 eseqdic = (struct RkRxDic *)0;
393
394 throw_col(0);
395 clr_line();
396 if (keypad_fun) set_keypad_off();
397 set_scroll_region(0 , crow + conv_lines - 1);
398 kk_restore_cursor();
399 flush();
400 #ifdef TERMINFO
401 closeTermData();
402 #endif
403 }
404
epilogue()405 void epilogue() /* originally defined in epilogue.c */
406 {
407 epilogue_no_close();
408 }
409
410 extern int ptyfd, ttyfd;
411
412 static void
ptyout(s,n)413 ptyout(s, n)
414 w_char *s;
415 int n;
416 {
417 int ml;
418
419 if ((ml = (*code_trans[(internal_code << 2) | pty_c_flag])
420 (buf, s, n * sizeof(w_char))) > 0) {
421 write(ptyfd, buf, ml);
422 }
423 }
424
425 static void
ttyout(s,n)426 ttyout(s, n)
427 w_char *s;
428 int n;
429 {
430 int ml;
431
432 if ((ml = (*code_trans[(internal_code << 2) | tty_c_flag])
433 (buf, s, n * sizeof(w_char))) > 0) {
434 write(ttyfd, buf, ml);
435 }
436 }
437
438 static int cursor_saved = 0;
439
440 static void
cursor_restore_if_saved()441 cursor_restore_if_saved()
442 {
443 if (cursor_saved) {
444 restore_cursor_raw();
445 flush();
446 cursor_saved = 0;
447 }
448 }
449
450 static int
cursor_save_if_not_saved()451 cursor_save_if_not_saved()
452 {
453 if (!cursor_saved) {
454 save_cursor_raw();
455 flush();
456 cursor_saved = 1;
457 return 1;
458 }
459 return 0;
460 }
461
462 #if defined(KC_DISCONNECTSERVER) && defined(KanjiThroughInfo)
463 #define MAXTHROUGHCOUNT 300
464 static int throughcount = 1;
465 #endif
466
467 #define MAXSEQUENCELEN 8
468 static char seqbuf[MAXSEQUENCELEN];
469 static int spooled; /* treated mainly in keyin1(); */
470
471 static void
normalize(dstat)472 normalize(dstat)
473 struct linebuf *dstat;
474 {
475 switch (dstat->revLen) {
476 case 0:
477 dstat->cursorPos = dstat->revPos = dstat->length;
478 break;
479 case 1:
480 dstat->cursorPos = dstat->revPos;
481 dstat->revLen = 0;
482 break;
483 default:
484 dstat->cursorPos = dstat->revPos;
485 break;
486 }
487 }
488
489 static int
diff(pr,cr)490 diff(pr, cr)
491 struct linebuf *pr, *cr;
492 {
493 w_char *pstr, *cstr;
494 int maxUnchanged, i;
495 int pRevPos = pr->revPos, cRevPos = cr->revPos;
496
497 if (pr->revLen == 0) {
498 pRevPos = pr->length;
499 }
500 if (cr->revLen == 0) {
501 cRevPos = cr->length;
502 }
503
504 if (pRevPos == cRevPos) {
505 if (pr->revLen == cr->revLen) {
506 if (pr->length < cr->length) {
507 maxUnchanged = pr->length;
508 }
509 else {
510 maxUnchanged = cr->length;
511 }
512 }
513 else if (pr->revLen < cr->revLen) {
514 maxUnchanged = pRevPos + pr->revLen;
515 }
516 else {
517 maxUnchanged = cRevPos + cr->revLen;
518 }
519 }
520 else if (pRevPos < cRevPos) {
521 maxUnchanged = pRevPos;
522 }
523 else {
524 maxUnchanged = cRevPos;
525 }
526
527 pstr = pr->line;
528 cstr = cr->line;
529
530 for (i = 0 ; i < maxUnchanged ; i++) {
531 if (*pstr++ != *cstr++) {
532 break;
533 }
534 }
535 cr->unchangedLeft = i;
536
537 if (i == pr->length || i == cr->length) {
538 cr->unchangedRight = 0;
539 return pr->length != cr->length;
540 }
541 else if (pr->length - pRevPos - pr->revLen ==
542 cr->length - cRevPos - cr->revLen) {
543 if (pr->length - pRevPos == cr->length - cRevPos) {
544 if (pr->length < cr->length) {
545 maxUnchanged = pr->length;
546 }
547 else {
548 maxUnchanged = cr->length;
549 }
550 }
551 else if (pr->length - pRevPos < cr->length - cRevPos) {
552 maxUnchanged = pr->length - pRevPos;
553 }
554 else {
555 maxUnchanged = cr->length - cRevPos;
556 }
557 }
558 else if (pr->length - pRevPos - pr->revLen <
559 cr->length - cRevPos - cr->revLen) {
560 maxUnchanged = pr->length - pRevPos - pr->revLen;
561 }
562 else {
563 maxUnchanged = cr->length - cRevPos - cr->revLen;
564 }
565 if (maxUnchanged > cr->length - cr->unchangedLeft) {
566 maxUnchanged = cr->length - cr->unchangedLeft;
567 }
568 if (maxUnchanged > pr->length - cr->unchangedLeft) {
569 maxUnchanged = pr->length - cr->unchangedLeft;
570 }
571
572 pstr = pr->line + pr->length;
573 cstr = cr->line + cr->length;
574
575 for (i = 0 ; i < maxUnchanged ; i++) {
576 if (*--pstr != *--cstr) {
577 break;
578 }
579 }
580 cr->unchangedRight = i;
581 return 1;
582 }
583
584 /*
585 check_redraw -- to check the difference from the previous display.
586
587 check_redraw have a side effect, that is, it sets display data from
588 ks to lbc.
589 */
590
591 int
check_redraw(ks,lbc,lbp)592 check_redraw(ks, lbc, lbp)
593 wcKanjiStatus *ks;
594 struct linebuf *lbp, *lbc;
595 {
596 int result = NOREDRAW;
597 w_char *modstr = lbc->mode_string;
598
599 if (ks->info & KanjiModeInfo) {
600 int width, length;
601
602 WStrcpy(modstr, ks->mode);
603 length = WStrlen(modstr);
604 width = colwidth(modstr, length);
605
606 while (width < maxmodelen) {
607 modstr[length++] = (w_char)' ';
608 width++;
609 }
610 modstr[length] = (w_char)'\0';
611 if (WStrcmp(modstr, lbp->mode_string)) {
612 result = FULLREDRAW;
613 }
614 lbc->mode_string_length = length;
615 lbc->mode_string_width = width;
616 }
617 else {
618 WStrcpy(modstr, lbp->mode_string);
619 lbc->mode_string_length = lbp->mode_string_length;
620 lbc->mode_string_width = lbp->mode_string_width;
621 }
622
623 if ((ks->info & KanjiGLineInfo) && ks->gline.length > 0) {
624 Strncpy(lbc->line, ks->gline.line, ks->gline.length);
625 lbc->line[ks->gline.length] = (w_char)0;
626 lbc->length = ks->gline.length;
627 lbc->revLen = ks->gline.revLen;
628 lbc->revPos = ks->gline.revPos;
629 }
630 else if (ks->length >= 0) {
631 Strncpy(lbc->line, ks->echoStr, ks->length);
632 lbc->line[ks->length] = (w_char)0;
633 lbc->length = ks->length;
634 lbc->revPos = ks->revPos;
635 lbc->revLen = ks->revLen;
636 }
637 else {
638 WStrcpy(lbc->line, lbp->line);
639 lbc->length = lbp->length;
640 lbc->revPos = lbp->revPos;
641 lbc->revLen = lbp->revLen;
642 }
643 normalize(lbc);
644 lbc->unchangedLeft = lbc->unchangedRight = 0;
645 if (result == NOREDRAW) {
646 if (diff(lbp, lbc)) {
647 result = PARTIALREDRAW;
648 }
649 }
650 return result;
651 }
652
653 static void
cursorWarp(lbc,to)654 cursorWarp(lbc, to)
655 struct linebuf *lbc;
656 int to;
657 {
658 int pos;
659
660 pos = colwidth(lbc->line + lbc->displayLeft, to - lbc->displayLeft) +
661 maxmodelen + 1;
662 throw_cur_raw(pos, crow);
663 flush();
664 }
665
666 static void
cursorMoveForward(lbc,from,to)667 cursorMoveForward(lbc, from, to)
668 struct linebuf *lbc;
669 int from, to;
670 {
671 int n;
672
673 if ((n = to - from) > 0) {
674 if (n < 8 &&
675 (lbc->revLen == 0 ||
676 to <= lbc->revPos ||
677 lbc->revPos + lbc->revLen <= from) ) {
678 /* confirm that this doesn't cross the reversed area */
679 ttyout(lbc->line + from, n);
680 }
681 else {
682 cursorWarp(lbc, to);
683 }
684 }
685 }
686
687 /*
688 cursorMove -- to move cursor.
689
690 Note: lbc->displayLeft should be fixed before this function is called.
691 */
692
693 static void
cursorMove(lbc,from,to)694 cursorMove(lbc, from, to)
695 struct linebuf *lbc;
696 int from, to;
697 {
698 if (to < from) {
699 cursorWarp(lbc, to);
700 }
701 else {
702 cursorMoveForward(lbc, from, to);
703 }
704 }
705
706 static void
adjust_reverse(length,revPos,revLen,uLeft)707 adjust_reverse(length, revPos, revLen, uLeft)
708 int length, *revPos, *revLen, uLeft;
709 {
710 if (*revPos < uLeft) {
711 *revLen -= uLeft - *revPos;
712 if (*revLen < 0) *revLen = 0;
713 *revPos = 0;
714 }
715 else {
716 *revPos -= uLeft;
717 }
718
719 if (*revPos > length) {
720 *revPos = length;
721 }
722 else if (*revPos + *revLen > length) {
723 *revLen = length - *revPos;
724 }
725 }
726
727 static void
redraw_it(gline,length,revPos,revLen)728 redraw_it(gline, length, revPos, revLen)
729 w_char *gline;
730 int length, revPos, revLen;
731 {
732 ttyout(gline, revPos);
733
734 if (revLen) {
735 h_r_on_raw();
736 flush();
737 ttyout(gline + revPos, revLen);
738 h_r_off_raw();
739 flush();
740 }
741
742 ttyout(gline + revPos + revLen, length - revPos - revLen);
743 }
744
745 /*
746 redraw -- to redraw display
747
748 redraw has a side effect, that is, it affects lbc->displayLeft and
749 lbc->displayRight.
750 */
751
752 static int
redraw(how,lbc,lbp)753 redraw(how, lbc, lbp)
754 int how;
755 struct linebuf *lbp, *lbc;
756 {
757 int restwidth, skips, ov;
758 w_char *gline = lbc->line;
759 int length = lbc->length;
760 int revLen = lbc->revLen;
761 int revPos = lbc->revPos;
762 int csrPos = lbc->cursorPos;
763 int uLeft = lbc->unchangedLeft;
764 int dLeft = lbp->displayLeft;
765
766 if (uLeft < dLeft || csrPos < dLeft ||
767 colwidth(gline + dLeft, revPos + revLen - dLeft) > maxwidth ||
768 (colwidth(gline + dLeft, csrPos - dLeft) >= maxwidth - 1 &&
769 revLen == 0 && length - revPos > 0)) {
770 how = FULLREDRAW;
771 }
772
773 if (!cursor_invisible_fun && revLen > 0) {
774 lbc->cursorPos = lbc->length;
775 }
776
777 switch (how) {
778 case NOREDRAW:
779 lbc->displayLeft = dLeft;
780 lbc->displayRight = lbp->displayRight;
781 cursorMove(lbc, lbp->cursorPos, lbc->cursorPos);
782 break;
783 case PARTIALREDRAW:
784 if (length == 0) {
785 lbc->displayLeft = lbc->displayRight = 0;
786 cursorMove(lbc, lbp->cursorPos, 0);
787 clr_end_screen();
788 flush();
789 }
790 else {
791 lbc->displayLeft = dLeft;
792 cursorMove(lbc, lbp->cursorPos, uLeft);
793
794 if (lbc->unchangedRight > lbp->length - lbp->displayRight &&
795 colwidth(gline + uLeft, length - uLeft - lbc->unchangedRight) ==
796 colwidth(lbp->line + uLeft,
797 lbp->length - uLeft - lbc->unchangedRight)) {
798 /* The width of changed area is the same as the previous one */
799 gline += uLeft;
800 length -= uLeft + lbc->unchangedRight;
801 adjust_reverse(length, &revPos, &revLen, uLeft);
802
803 lbc->displayRight = lbp->displayRight + lbc->length - lbp->length;
804
805 redraw_it(gline, length, revPos, revLen);
806 if (lbc->cursorPos != lbc->length - lbc->unchangedRight) {
807 if (lbc->cursorPos > lbc->displayRight) {
808 cursorWarp(lbc, lbc->displayRight);
809 ttyout(rightover, 1);
810 }
811 else {
812 cursorWarp(lbc, lbc->cursorPos);
813 }
814 }
815 }
816 else {
817 restwidth = maxwidth;
818 restwidth -= colwidth(gline + dLeft, uLeft - dLeft);
819 gline += uLeft;
820 skips = skipchars(gline, restwidth, &ov);
821 if (ov > 0) {
822 skips -= ov;
823 }
824 length = skips;
825 adjust_reverse(length, &revPos, &revLen, uLeft);
826 lbc->displayRight = uLeft + length;
827
828 redraw_it(gline, length, revPos, revLen);
829 if (lbc->displayRight < lbc->length) {
830 ttyout(rightover, 1);
831 }
832 clr_end_screen();
833 flush();
834 if (lbc->cursorPos != lbc->length &&
835 lbc->cursorPos != lbc->displayRight) {
836 cursorWarp(lbc, lbc->cursorPos);
837 }
838 }
839 }
840 break;
841 case FULLREDRAW:
842 throw_cur_raw(0, crow);
843 flush();
844
845 ttyout(lbc->mode_string, lbc->mode_string_length);
846
847 lbc->displayLeft = 0;
848 lbc->displayRight = length;
849
850 if (length > 0) {
851 int l, l1, l2;
852
853 l = colwidth(gline, length);
854 if (l > maxwidth) {
855
856 /* In this condition, it is impossible to display the whole
857 pre-edit characters. Some part of the pre-edit string will
858 be cut */
859
860 l1 = colwidth(gline, revPos);
861 l2 = revLen > 0 ? colwidth(gline + revPos, revLen) : 0;
862
863 if (cursor_invisible_fun) {
864 if (revLen > 1) {
865 cursor_invisible_raw();
866 }
867 else {
868 cursor_normal_raw();
869 }
870 }
871
872 if (l2 > maxwidth) {
873 /* Align to the right border of reverse area. */
874 skips = skipchars(gline + revPos, l2 - maxwidth, &ov);
875 gline += revPos + skips;
876 revLen = length = revLen - skips;
877 lbc->displayLeft = revPos + skips;
878 revPos = 0;
879 #if 0
880 /* In case Aline to the left */
881 gline += revPos;
882 skips = skipchars(gline, maxwidth, &ov);
883 revLen = length = skips - ov;
884 lbc->displayLeft = revPos;
885 revPos = 0;
886 #endif
887 }
888 else if (l1 + l2 > maxwidth ||
889 (length - revPos - revLen > 0 && l1 >= maxwidth - 1)) {
890 /* Place reverse area to the middle of line. */
891 skips = skipchars(gline, l1 - (maxwidth - l2) / 2, &ov);
892 /* ov is not used */
893 gline += skips;
894 revPos -= skips;
895 length = skipchars(gline, maxwidth, &ov);
896 if (ov > 0) {
897 length -= ov;
898 }
899 lbc->displayLeft = skips;
900 }
901 else { /* length > maxwidth */
902 /* Just cut off the rest */
903 skips = skipchars(gline, maxwidth, &ov);
904 length = skips - ov;
905 lbc->displayLeft = 0;
906 }
907 lbc->displayRight = lbc->displayLeft + length;
908 }
909
910 if (lbc->displayLeft > 0) {
911 ttyout(leftover, 1);
912 }
913 else {
914 ttyout(lrok, 1);
915 }
916
917 redraw_it(gline, length, revPos, revLen);
918 if (lbc->displayRight < lbc->length) {
919 ttyout(rightover, 1);
920 }
921 }
922 clr_end_screen();
923 flush();
924
925 if (lbc->length != lbc->cursorPos && lbc->displayRight != lbc->cursorPos) {
926 cursorWarp(lbc, lbc->cursorPos);
927 }
928 break;
929 }
930 return 0;
931 }
932
933 /*
934
935 The following function t_print_l_normal is originally defined in
936 functions.c in uum source code.
937
938 */
939
940 int
t_print_l_normal()941 t_print_l_normal()
942 {
943 cursor_restore_if_saved();
944 save_cursor_raw();
945 flush();
946 redraw(FULLREDRAW, lbuf + curlbuf, lbuf + prevlbuf);
947 restore_cursor_raw();
948 flush();
949 return 0;
950 }
951
952 char *
romkan_dispmode()953 romkan_dispmode()
954 {
955 return (char *)"\244\253\244\363\244\312";
956 /* "�����" in EUC */
957 }
958
959 char *
romkan_offmode()960 romkan_offmode()
961 {
962 return romkan_dispmode();
963 }
964
965 /*
966
967 The following two functions are defined originally in etc/msg.c.
968 And these two functions are for messaging facility. Here canna
969 rewrote them as dummy functions.
970
971 struct msg_cat *msg_open();
972 char *get_msg();
973
974 */
975
976 struct msg_cat *
msg_open(name,nlspath,lang)977 msg_open(name, nlspath, lang) /* originally defined in etc/msg.c */
978 char *name;
979 char *nlspath;
980 char *lang;
981 /* ARGSUSED */
982 {
983 return 0;
984 }
985
986 char *
msg_get(cad,n,mesg,lang)987 msg_get(cad, n, mesg, lang) /* originally defined in etc/msg.c */
988 struct msg_cat *cad;
989 int n;
990 char *mesg;
991 register char *lang;
992 /* ARGSUSED */
993 {
994
995
996 static char *msgtbl[] = {
997 "\r\243\343\243\341\243\356\243\365\243\365\243\355(\244\253\244\312\264\301\273\372\312\321\264\271\245\325\245\355\245\363\245\310\245\250\245\363\245\311\245\327\245\355\245\273\245\303\245\265)\r\n",
998 /* "\r���������(���ʴ����Ѵ��ե��ȥ���ɥץ��å�)\r\n", */
999
1000 "Malloc\244\313\274\272\307\324\244\267\244\336\244\267\244\277\241\243",
1001 /* "Malloc�˼��Ԥ��ޤ�����", */
1002
1003 "\r\n\243\343\243\341\243\356\243\365\243\365\243\355\244\362\275\252\244\357\244\352\244\336\244\271\241\243\r\n",
1004 /* "\r\n�����������ޤ���\r\n", */
1005
1006 "\243\365\243\365\243\355\244\253\244\351\243\365\243\365\243\355\244\317\265\257\244\263\244\273\244\336\244\273\244\363\241\243\n",
1007 /* "�������������ϵ������ޤ���\n", */
1008
1009 "",
1010 "",
1011 "",
1012 " (\307\241\262\277)",
1013 /* " (ǡ��)", */
1014
1015 "",
1016 };
1017 static int msgtblsize = sizeof(msgtbl) / sizeof(char *);
1018
1019 if (n <= 0 || msgtblsize < n) {
1020 return "";
1021 }
1022 else {
1023 return msgtbl[n - 1];
1024 }
1025 }
1026
1027 char *
get_kbd_env()1028 get_kbd_env() /* originally defined in wnnrc_op.c */
1029 {
1030 extern char *getenv();
1031 return getenv("TERM");
1032 }
1033
1034 typedef struct {
1035 char *seq;
1036 int id;
1037 } SeqToID;
1038
1039 #define INITIALSIZE 256
1040
1041 static struct RkRxDic *
RkCreateRoma(keywords,n)1042 RkCreateRoma(keywords, n)
1043 SeqToID *keywords;
1044 int n;
1045 {
1046 struct RkRxDic *rdic;
1047 unsigned char *p;
1048 int i;
1049
1050 rdic = (struct RkRxDic *)malloc(sizeof(struct RkRxDic));
1051 if (rdic) {
1052 rdic->dic = RX_KPDIC;
1053 rdic->nr_nkey = n;
1054 rdic->nr_strsz = INITIALSIZE;
1055 rdic->nr_string = (unsigned char *)malloc(INITIALSIZE);
1056 if (rdic->nr_string) {
1057 rdic->nr_brules = (unsigned char *)0;
1058 rdic->nr_bchars = rdic->nr_string;
1059
1060 p = rdic->nr_string;
1061 *p = (unsigned char)0; p++;
1062 for (i = 0 ; i < n ; i++) {
1063 int len;
1064
1065 len = strlen(keywords[i].seq);
1066 while (p + len + 4 > rdic->nr_string + rdic->nr_strsz) {
1067 int offset = p - rdic->nr_string;
1068 rdic->nr_string =
1069 (unsigned char *)realloc(rdic->nr_string,
1070 rdic->nr_strsz + INITIALSIZE);
1071 if (!rdic->nr_string) {
1072 goto exit_nr_string;
1073 }
1074 rdic->nr_strsz += INITIALSIZE;
1075 p = rdic->nr_string + offset;
1076 }
1077 strcpy((char *)p, keywords[i].seq);
1078 p += len + 1;
1079 *p++ = (unsigned char)keywords[i].id;
1080 *p++ = (unsigned char)0;
1081 *p++ = (unsigned char)0; /* for temp and bang */
1082 }
1083 rdic->nr_strsz = p - rdic->nr_string;
1084 rdic->nr_string = (unsigned char *)realloc(rdic->nr_string,
1085 rdic->nr_strsz);
1086 if (!rdic->nr_string) {
1087 goto exit_nr_string;
1088 }
1089
1090 rdic->nr_keyaddr =
1091 (unsigned char **)calloc((unsigned)n + 1, sizeof(unsigned char *));
1092 /* n + 1 �ˤ����Τ� alloc(0) �Υ����å������ݤʤ��� */
1093 if (rdic->nr_keyaddr) {
1094 for (i = 0, p = rdic->nr_string + 1 ; i < n ; i++) {
1095 rdic->nr_keyaddr[i] = p;
1096 while ( *p++ ); /* roma */
1097 while ( *p++ ); /* kana */
1098 while ( *p++ ); /* temp */
1099 }
1100 return rdic;
1101 }
1102 exit_nr_string:
1103 free((char *)rdic->nr_string);
1104 }
1105 free((char *)rdic);
1106 rdic = (struct RkRxDic *)0;
1107 }
1108
1109 return rdic;
1110 }
1111
1112 #define INITIALKEYS 128
1113
1114 static SeqToID *sequences;
1115 static int nsequences = 0, seqsize = 0;
1116
1117 static int
compar(p,q)1118 compar(p, q)
1119 SeqToID *p, *q;
1120 {
1121 char *s = p->seq;
1122 char *t = q->seq;
1123
1124 while ( *s == *t )
1125 if ( *s )
1126 s++, t++;
1127 else
1128 return 0;
1129 return ((int)*s) - ((int)*t);
1130 }
1131
1132 void
registerkeys()1133 registerkeys()
1134 {
1135 qsort((char *)sequences, nsequences, sizeof(SeqToID), compar);
1136 eseqdic = RkCreateRoma(sequences, nsequences);
1137 }
1138
1139 static
cannakeyentry(s,ident)1140 cannakeyentry(s, ident)
1141 char *s;
1142 int ident;
1143 {
1144 if (!s || s[0] != '\033' || !s[1]) {
1145 return -1;
1146 }
1147 s++;
1148 while (!(nsequences < seqsize)) {
1149 sequences =
1150 (seqsize == 0) ?
1151 (SeqToID *)malloc(INITIALKEYS * sizeof(SeqToID)) :
1152 (SeqToID *)realloc(sequences,
1153 (seqsize + INITIALKEYS) * sizeof(SeqToID));
1154 if (sequences) {
1155 seqsize += INITIALKEYS;
1156 }
1157 else {
1158 seqsize = 0;
1159 return -1;
1160 }
1161 }
1162
1163 sequences[nsequences].seq = malloc(strlen(s) + 1);
1164 if (sequences[nsequences].seq) {
1165 strcpy(sequences[nsequences].seq, s);
1166 sequences[nsequences].id = ident;
1167 nsequences++;
1168 return 0;
1169 }
1170 else {
1171 return -1;
1172 }
1173 }
1174
1175 void
cannakeydef(xterm,term,seq,id)1176 cannakeydef(xterm, term, seq, id)
1177 int xterm;
1178 char *term, *seq;
1179 int id;
1180 {
1181 if (xterm == CANNA_CTERMINAL) {
1182 if (terminalname && !strcmp(terminalname, term)) {
1183 cannakeyentry(seq, id);
1184 }
1185 }
1186 }
1187
1188 /*
1189
1190 convert_getterm is called from termio.c and termcap.c.
1191
1192 Here provides a dummy convert_getterm().
1193
1194 */
1195
1196 #define MAXSEQUENCE 20
1197 #define AREASIZE 1024
1198
1199 int
convert_getterm(term,flag)1200 convert_getterm(term, flag) /* originally defined in conv/cvt_read.c */
1201 char *term;
1202 int flag;
1203 /* ARGSUSED */
1204 {
1205 #ifdef TERMCAP
1206 char xx[MAXSEQUENCE], *p = xx, *q, *tgetstr();
1207
1208 char tcaparea[AREASIZE];
1209
1210 if (tgetent(tcaparea, term) > 0) {
1211 p = xx; if (q = tgetstr("k1", &p)) cannakeyentry(q, CANNA_KEY_F1);
1212 p = xx; if (q = tgetstr("k2", &p)) cannakeyentry(q, CANNA_KEY_F2);
1213 p = xx; if (q = tgetstr("k3", &p)) cannakeyentry(q, CANNA_KEY_F3);
1214 p = xx; if (q = tgetstr("k4", &p)) cannakeyentry(q, CANNA_KEY_F4);
1215 p = xx; if (q = tgetstr("k5", &p)) cannakeyentry(q, CANNA_KEY_F5);
1216 p = xx; if (q = tgetstr("k6", &p)) cannakeyentry(q, CANNA_KEY_F6);
1217 p = xx; if (q = tgetstr("k7", &p)) cannakeyentry(q, CANNA_KEY_F7);
1218 p = xx; if (q = tgetstr("k8", &p)) cannakeyentry(q, CANNA_KEY_F8);
1219 p = xx; if (q = tgetstr("k9", &p)) cannakeyentry(q, CANNA_KEY_F9);
1220 p = xx; if (q = tgetstr("k;", &p)) cannakeyentry(q, CANNA_KEY_F10);
1221 p = xx; if (q = tgetstr("ku", &p)) cannakeyentry(q, CANNA_KEY_Up);
1222 p = xx; if (q = tgetstr("kr", &p)) cannakeyentry(q, CANNA_KEY_Right);
1223 p = xx; if (q = tgetstr("kl", &p)) cannakeyentry(q, CANNA_KEY_Left);
1224 p = xx; if (q = tgetstr("kd", &p)) cannakeyentry(q, CANNA_KEY_Down);
1225 p = xx; if (q = tgetstr("kF", &p)) cannakeyentry(q, CANNA_KEY_Rollup);
1226 p = xx; if (q = tgetstr("kR", &p)) cannakeyentry(q, CANNA_KEY_Rolldown);
1227 #ifdef CANNA_KEY_PageDown
1228 p = xx; if (q = tgetstr("kN", &p)) cannakeyentry(q, CANNA_KEY_PageDown);
1229 p = xx; if (q = tgetstr("kP", &p)) cannakeyentry(q, CANNA_KEY_PageUp);
1230 #endif
1231 p = xx; if (q = tgetstr("kh", &p)) cannakeyentry(q, CANNA_KEY_Home);
1232 p = xx; if (q = tgetstr("%1", &p)) cannakeyentry(q, CANNA_KEY_Help);
1233 p = xx; if (q = tgetstr("kI", &p)) cannakeyentry(q, CANNA_KEY_Insert);
1234 #ifdef CANNA_KEY_End
1235 p = xx; if (q = tgetstr("@7", &p)) cannakeyentry(q, CANNA_KEY_End);
1236 #endif
1237 }
1238 #endif
1239
1240 #ifdef TERMINFO
1241 int fd, res;
1242
1243 fd = open("/dev/null", O_WRONLY, &res);
1244 setupterm(term, fd, (int *)0);
1245
1246 cannakeyentry(key_f1, CANNA_KEY_F1);
1247 cannakeyentry(key_f2, CANNA_KEY_F2);
1248 cannakeyentry(key_f3, CANNA_KEY_F3);
1249 cannakeyentry(key_f4, CANNA_KEY_F4);
1250 cannakeyentry(key_f5, CANNA_KEY_F5);
1251 cannakeyentry(key_f6, CANNA_KEY_F6);
1252 cannakeyentry(key_f7, CANNA_KEY_F7);
1253 cannakeyentry(key_f8, CANNA_KEY_F8);
1254 cannakeyentry(key_f9, CANNA_KEY_F9);
1255 cannakeyentry(key_f10, CANNA_KEY_F10);
1256 cannakeyentry(key_up, CANNA_KEY_Up);
1257 cannakeyentry(key_right, CANNA_KEY_Right);
1258 cannakeyentry(key_left, CANNA_KEY_Left);
1259 cannakeyentry(key_down, CANNA_KEY_Down);
1260 cannakeyentry(key_home, CANNA_KEY_Home);
1261 cannakeyentry(key_help, CANNA_KEY_Help);
1262 cannakeyentry(key_sf, CANNA_KEY_Rollup);
1263 cannakeyentry(key_sr, CANNA_KEY_Rolldown);
1264 #ifdef CANNA_KEY_PageDown
1265 cannakeyentry(key_npage, CANNA_KEY_PageDown);
1266 cannakeyentry(key_ppage, CANNA_KEY_PageUp);
1267 #endif
1268 cannakeyentry(key_ic, CANNA_KEY_Insert);
1269 #ifdef CANNA_KEY_End
1270 cannakeyentry(key_end, CANNA_KEY_End);
1271 #endif
1272
1273 resetterm();
1274 #endif
1275
1276 if (terminalname = malloc(strlen(term) + 1)) {
1277 strcpy(terminalname, term);
1278 }
1279
1280 return 0;
1281 }
1282
1283 int
1284 keyin1(gch, yyy) /* originally defined in conv/cvt_read.c */
1285 int (*gch) pro((void));
1286 char *yyy; /* ARGSUSED */
1287 {
1288 int ch, n, dummy1, dummy2, dummy3;
1289 char xxx[MAXSEQUENCELEN];
1290
1291 if (spooled && seqbuf[spooled]) {
1292 return seqbuf[spooled++];
1293 }
1294 while ((ch = (*gch)()) < 0)
1295 ;
1296 if (ch == 0x1b && eseqdic) {
1297 int i = 1, res;
1298
1299 seqbuf[0] = 0x1b;
1300 seqbuf[1] = 0;
1301 do {
1302 ch = (*gch)();
1303 if (ch < 0) {
1304 break;
1305 }
1306 seqbuf[i++] = ch;
1307 seqbuf[i] = '\0';
1308 res = RkMapPhonogram(eseqdic, xxx, MAXSEQUENCELEN, seqbuf + 1, i - 1,
1309 0, 0, &n, &dummy1, &dummy2, &dummy3);
1310 } while (!n && i < MAXSEQUENCELEN - 1);
1311 if (!(ch < 0) && res) {
1312 spooled = 0;
1313 return (int)xxx[0] & 0xff;
1314 }
1315 else {
1316 seqbuf[0] = '\0';
1317 spooled = 1;
1318 return 0x1b;
1319 }
1320 }
1321 seqbuf[0] = '\0';
1322 return ch;
1323 }
1324
1325 void
canna_mainloop()1326 canna_mainloop()
1327 {
1328 w_char workbuf[MAXSIZE];
1329 int wch, ml, howtoredraw;
1330 wcKanjiStatus ks;
1331
1332 for (;;) {
1333 #ifdef MAXTHROUGHCOUNT
1334 if (throughcount) {
1335 if (throughcount > MAXTHROUGHCOUNT) {
1336 wcKanjiControl(0, KC_DISCONNECTSERVER, 0);
1337 throughcount = 0;
1338 }
1339 else {
1340 throughcount++;
1341 }
1342 }
1343 #endif
1344
1345 /* keyin is wrong. keyin can not treat G3 code correctly.
1346 keyin should be modified someday. */
1347 wch = keyin();
1348 if (wch != -1) {
1349 if (wch & 0x8000) { /* G1 or G3 kanji is entered. */
1350 w_char xx[2];
1351
1352 cursor_restore_if_saved();
1353 xx[0] = (w_char)wch;
1354 xx[1] = (w_char)0;
1355 ptyout(xx, 1);
1356 }
1357 else {
1358 ml = wcKanjiString(0, wch, workbuf, MAXSIZE, &ks);
1359
1360 #ifdef MAXTHROUGHCOUNT
1361 if (!(ks.info & KanjiThroughInfo)) {
1362 throughcount = 1;
1363 }
1364 #endif
1365
1366 curlbuf = prevlbuf; /* Note: prevlbuf is a macro */
1367
1368 howtoredraw = check_redraw(&ks, lbuf + curlbuf, lbuf + prevlbuf);
1369 if (howtoredraw
1370 || lbuf[curlbuf].cursorPos != lbuf[prevlbuf].cursorPos) {
1371 if (cursor_save_if_not_saved()) {
1372 howtoredraw = FULLREDRAW;
1373 }
1374 redraw(howtoredraw, lbuf + curlbuf, lbuf + prevlbuf);
1375 if (lbuf[curlbuf].length == 0) {
1376 cursor_restore_if_saved();
1377 }
1378 }
1379 if (ml > 0) {
1380 cursor_restore_if_saved();
1381 if ((ks.info & KanjiThroughInfo) && seqbuf[0]) {
1382 write(ptyfd, seqbuf, strlen(seqbuf));
1383 }
1384 else {
1385 ptyout(workbuf, ml);
1386 }
1387 }
1388 }
1389 }
1390 }
1391 }
1392
1393
1394 /* dummy function definitions are below this line */
1395
1396 /*
1397
1398 The following 3 functions are defined in Wnn system.
1399
1400 Canna rewrote them.
1401
1402 char *wnn_perror();
1403 char *get_server_env();
1404 char *get_kbd_env();
1405
1406 */
1407
1408 char *
wnn_perror()1409 wnn_perror()
1410 {
1411 return "??";
1412 }
1413
1414 char *
get_server_env(lang)1415 get_server_env(lang) /* originally defined in etc/server_env.c */
1416 char *lang;
1417 /* ARGSUSED */
1418 {
1419 return "CANNAHOST";
1420 }
1421
1422 int
hani_settei_normal(c_b)1423 hani_settei_normal(c_b) /* originally defined in touroku.c */
1424 /* struct buf *c_b; */
1425 /* ARGSUSED */
1426 {
1427 return 0;
1428 }
1429
1430 int
initial_message_out()1431 initial_message_out() /* originally defined in prologue.c */
1432 {
1433 return 1; /* dummy function */
1434 }
1435
1436 int
set_cur_env(s)1437 set_cur_env(s) /* originally defined in uif.c. */
1438 char s;
1439 /* ARGSUSED */
1440 {
1441 return 0;
1442 }
1443
1444 /*
1445
1446 The following functions are originally defined in functions.c, and
1447 refered from header.c.
1448
1449 */
1450
char_len_normal(x)1451 int char_len_normal(x) w_char x; /* ARGSUSED */ { return 0; }
1452
c_top_normal()1453 int c_top_normal() { return 0; }
c_end_normal()1454 int c_end_normal() { return 0; }
call_t_print_l_normal(x,add)1455 int call_t_print_l_normal(x, add) int x, add; /* ARGSUSED */ { return 0; }
char_q_len_normal(x)1456 int char_q_len_normal(x) w_char x; /* ARGSUSED */ { return 0; }
call_jl_yomi_len()1457 int call_jl_yomi_len() { return 0; }
1458
t_redraw_move_normal(x,start,end,clr_l)1459 int t_redraw_move_normal(x , start , end,clr_l)
1460 int x, start, end, clr_l; /* ARGSUSED */ { return 0; }
call_t_redraw_move_normal(x,start,end,clt_l,add)1461 int call_t_redraw_move_normal(x, start, end, clt_l, add)
1462 int x, start, end, clt_l, add; /* ARGSUSED */ { return 0; }
call_t_redraw_move_1_normal(x,start,end,clt_l,add1,add2,mode)1463 int call_t_redraw_move_1_normal(x, start, end, clt_l, add1, add2, mode)
1464 int x, start, end, clt_l, add1, add2, mode; /* ARGSUSED */ { return 0; }
call_t_redraw_move_2_normal(x,start1,start2,end1,end2,clt_l,add)1465 int call_t_redraw_move_2_normal(x, start1, start2, end1, end2, clt_l, add)
1466 int x, start1, start2, end1, end2, clt_l, add; /* ARGSUSED */ { return 0; }
1467
call_redraw_line_normal(x,add)1468 int call_redraw_line_normal(x, add) int x, add; /* ARGSUSED */ { return 0; }
1469
1470
debugprint(fmt,a,b,c)1471 debugprint(fmt, a, b, c)
1472 char *fmt, *a, *b, *c;
1473 {
1474 FILE *f, *fopen();
1475
1476 f = fopen("/tmp/kon", "a");
1477 fprintf(f, fmt, a, b, c);
1478 fclose(f);
1479 }
1480