1 #ifndef lint
2 static char sccs_id[] = "%W% (TriChlor) %G% %U%";
3 #endif /* lint */
4
5 #ifdef _WINDOWS
6 #include <windows.h>
7 #include <string.h>
8 #endif /* _WINDOWS */
9 #include <stdio.h>
10 #include "portable.h"
11 #include "vnkeys.h"
12 #include "vncompos.h"
13
14 #if VK_DEBUG /* NOT ifdef! */
15 static int Vk_debug = 0;
16 #endif
17
18 static Vk_Fsm Id[VK_MAX_FSMS];
19 static u_char Fsm_in_use[VK_MAX_FSMS];
20 static Vk_State State[VK_MAX_FSMS];
21 static Vk_State Prev_State[VK_MAX_FSMS];
22 static u_char Com[VK_MAX_FSMS]; /* composition announcer key */
23 static u_char Bs[VK_MAX_FSMS]; /* configurable backspace */
24 static u_char Batch[VK_MAX_FSMS]; /* batch or interactive, def = batch */
25 static u_char Prev_temp[VK_MAX_FSMS]; /* for EXPLICIT_COMPOSE state */
26 /* Make Co_data publicly available for diacritic manipulations! */
27 Co_Data FAR Co_data[VK_MAX_FSMS]; /* storage area for composition machinery */
28
29 #if VK_DEBUG
30 static void print_state();
31 #endif
32
33
34 #define output_ch(ch, outchars, outcount) \
35 (outchars)[(*(outcount))++] = (ch)
36
37 /*
38 * vk_init: Initialize the state machine in the given state "st",
39 * using "com" as the
40 * compose key, and "bs" as the backspace character to be
41 * sent out when needed in immediate echo mode.
42 *
43 * Returns ID of FSM if successful, -1 if failed.
44 */
45 Vk_Fsm FAR PASCAL
46 #ifdef ANSI_C
vk_init(Vk_State st,u_char com,u_char bs)47 vk_init(Vk_State st, u_char com, u_char bs)
48 #else
49 vk_init(st, com, bs)
50 Vk_State st;
51 u_char com;
52 u_char bs;
53 #endif
54 {
55 Vk_Fsm id;
56
57 for (id = 0; id < VK_MAX_FSMS; id++) {
58 if (!Fsm_in_use[id]) break;
59 }
60
61 if (id == VK_MAX_FSMS) return(-1);
62 Fsm_in_use[id] = 1;
63 State[id] = st;
64 Com[id] = com;
65 Bs[id] = bs;
66 Batch[id] = 0; /* by default it's interactive */
67 Prev_temp[id] = 0; /* in case it's initialized mid-stream */
68
69 #if VK_DEBUG
70 if (Vk_debug) {
71 print_state(st, State[id]);
72 printf("Com[id] = %d, Bs[id] = %d\n", Com[id], Bs[id]);
73 }
74 #endif
75
76 vncompose_init((Co_Data FAR *)&Co_data[id]); /* initialize the composition tables */
77
78 return(id);
79 }
80
81
82 /*
83 * vk_step: Takes one input key "inkey" and operates the state machine
84 * accordingly. The output, if any, is stored in the user-provided
85 * array "outchars", with the number of characters output in
86 * "outcount".
87 *
88 */
89 Vk_State FAR PASCAL
90 #ifdef ANSI_C
vk_step(Vk_Fsm id,u_char inkey,u_char FAR outchars[],int FAR * outcount)91 vk_step(Vk_Fsm id, u_char inkey, u_char FAR outchars[], int FAR *outcount)
92 #else
93 vk_step(id, inkey, outchars, outcount)
94 Vk_Fsm id;
95 u_char inkey;
96 u_char FAR outchars[];
97 int FAR *outcount;
98 #endif
99 {
100 u_char temp; /* for storate of composed character */
101 #ifdef _WINDOWS
102 static
103 #endif
104 int fl_compose; /* flag: in compose sequence */
105 #ifdef _WINDOWS
106 static
107 #endif
108 int fl_erase; /* flag: erase previous vowel */
109
110
111 #ifdef VK_DEBUG
112 if (Vk_debug) {
113 switch(inkey) {
114 case '\003': /* ctrl-c */
115 return(State[id]);
116 case '\011': /* ctrl-I */
117 if (Mode[id] == VK_MODE_DELAYED) Mode[id] = VK_MODE_IMMEDIATE;
118 else Mode[id] = VK_MODE_DELAYED;
119 printf("Mode = %d\n", Mode[id]);
120 return(State[id]);
121 }
122 }
123 #endif
124
125 *outcount = 0;
126 while (1) {
127
128 switch(State[id]) {
129
130 /***********************************************/
131
132 case VK_ST_LITERAL:
133 if (inkey == Com[id]) {
134 State[id] = VK_ST_ESC_LITERAL;
135 } else {
136 output_ch(inkey, outchars, outcount);
137 }
138 return(State[id]);
139
140 /***********************************************/
141
142 case VK_ST_ESC_LITERAL:
143 switch(inkey) {
144 case VK_ESC_VIETNAMESE_1:
145 case VK_ESC_VIETNAMESE_2:
146 State[id] = VK_ST_VIETNAMESE;
147 break;
148 case VK_ESC_ENGLISH_1:
149 case VK_ESC_ENGLISH_2:
150 State[id] = VK_ST_ENGLISH;
151 break;
152 case VK_ESC_LITERAL_1:
153 case VK_ESC_LITERAL_2:
154 State[id] = VK_ST_LITERAL;
155 break;
156 default:
157 State[id] = VK_ST_LITERAL;
158 output_ch(Com[id], outchars, outcount);
159 output_ch(inkey, outchars, outcount);
160 break;
161 }
162 return(State[id]);
163
164 /***********************************************/
165
166 case VK_ST_VIETNAMESE:
167 if (inkey == Com[id]) {
168 Prev_State[id] = VK_ST_VIETNAMESE;
169 State[id] = VK_ST_ESC_VIETMY;
170 } else {
171 temp = vncompose(&Co_data[id], inkey, (int FAR *) &fl_compose, (int FAR *) &fl_erase);
172 if (Batch[id] && fl_compose) { /* batch mode composition */
173 /* give it to EXPLICIT COMPOSITION, which delays the echo */
174 Prev_State[id] = VK_ST_VIETNAMESE;
175 State[id] = VK_ST_EXPLICIT_COMPOSE;
176 Prev_temp[id] = temp; /* save result since we're in delayed echo */
177 } else { /* interactive mode composition */
178 if (fl_erase) {
179 output_ch(Bs[id], outchars, outcount);
180 }
181 output_ch(temp, outchars, outcount);
182 }
183 }
184 return(State[id]);
185
186 /***********************************************/
187
188 case VK_ST_ENGLISH:
189 if (inkey == Com[id]) {
190 Prev_State[id] = VK_ST_ENGLISH;
191 State[id] = VK_ST_ESC_VIETMY;
192 } else {
193 output_ch(inkey, outchars, outcount);
194 }
195 return(State[id]);
196
197 /***********************************************/
198
199 case VK_ST_ESC_VIETMY:
200 switch(inkey) {
201 case VK_ESC_VIETNAMESE_1:
202 case VK_ESC_VIETNAMESE_2:
203 State[id] = VK_ST_VIETNAMESE;
204 return(State[id]);
205 case VK_ESC_ENGLISH_1:
206 case VK_ESC_ENGLISH_2:
207 State[id] = VK_ST_ENGLISH;
208 return(State[id]);
209 case VK_ESC_LITERAL_1:
210 case VK_ESC_LITERAL_2:
211 State[id] = VK_ST_LITERAL;
212 return(State[id]);
213 default:
214 /* we got <COM>x, so flush the compose machine first */
215 Prev_temp[id] = 0;
216 (void) vncompose(&Co_data[id], (u_char) 0, (int FAR *) &fl_compose, (int FAR *) &fl_erase);
217
218 if (inkey == Com[id]) {
219 output_ch(Com[id], outchars, outcount);
220 State[id] = Prev_State[id];
221 return(State[id]);
222 } else {
223 State[id] = VK_ST_EXPLICIT_COMPOSE;
224 break; /* we DON'T return, go back to start composing on this key */
225 }
226 }
227 break;
228
229 /***********************************************/
230
231 case VK_ST_EXPLICIT_COMPOSE:
232 temp = vncompose(&Co_data[id], inkey, (int FAR *) &fl_compose, (int FAR *) &fl_erase);
233 if (fl_compose) { /* composition sequence starting or continuing */
234 if (!fl_erase && Prev_temp[id]) {
235 /* we just ended a composition sequence, e.g., \ae */
236 output_ch(Prev_temp[id], outchars, outcount);
237 if (!Batch[id] && Prev_State[id] == VK_ST_VIETNAMESE) {
238 /* we're not really in delayed echo */
239 output_ch(temp, outchars, outcount);
240 State[id] = VK_ST_VIETNAMESE;
241 } else {
242 Prev_temp[id] = temp; /* save result since we're in delayed echo */
243 }
244 } else { /* did not just end a composition sequence */
245 Prev_temp[id] = temp; /* save result since we're in delayed echo */
246 }
247 } else { /* composition is finished */
248 State[id] = Prev_State[id];
249
250 /* Now, why was composition finished? */
251 if (fl_erase) { /* because it's completed, e.g., \e^' */
252 output_ch(temp, outchars, outcount);
253 } else if (temp == Com[id]) { /* because it's ended by \, e.g., \e^\ */
254 output_ch(Prev_temp[id], outchars, outcount);
255 State[id] = VK_ST_ESC_VIETMY;
256 } else { /* because it's ended by something else, e.g., \m or \e^m */
257 if (Prev_temp[id] != 0) {
258 output_ch(Prev_temp[id], outchars, outcount);
259 }
260 output_ch(temp, outchars, outcount);
261 }
262 }
263 return(State[id]);
264 } /* switch(St[id]) */
265 } /* forever */
266 }
267
268
269 /*
270 * vk_get: called when the user wishes to query the internal
271 * state of the FSM.
272 */
273 void FAR PASCAL
274 #ifdef ANSI_C
vk_get(Vk_Fsm id,u_int FAR * value,int get_flag)275 vk_get(Vk_Fsm id, u_int FAR *value, int get_flag)
276 #else
277 vk_get(id, value, get_flag)
278 Vk_Fsm id;
279 u_int FAR *value;
280 int get_flag;
281 #endif
282 {
283 if (get_flag & VK_GET_STATE) {
284 *((Vk_State FAR *) value) = State[id];
285 } else if (get_flag & VK_GET_ESC) {
286 *((u_char FAR *) value) = Com[id];
287 } else if (get_flag & VK_GET_BS) {
288 *((u_char FAR *) value) = Bs[id];
289 } else if (get_flag & VK_GET_BATCH) {
290 *((u_char FAR *) value) = Batch[id];
291 }
292 }
293
294
295 /*
296 * vk_set: called when the user wishes to set the internal
297 * state of the FSM.
298 */
299 void FAR PASCAL
300 #ifdef ANSI_C
vk_set(Vk_Fsm id,u_int value,int set_flag)301 vk_set(Vk_Fsm id, u_int value, int set_flag)
302 #else
303 vk_set(id, value, set_flag)
304 Vk_Fsm id;
305 u_int value;
306 int set_flag;
307 #endif
308 {
309 if (set_flag & VK_SET_STATE) {
310 State[id] = (Vk_State) value;
311 vk_flush(id, 0, 0); /* flush the FSM whenever we set the state */
312 } else if (set_flag & VK_SET_ESC) {
313 Com[id] = (u_char) value;
314 } else if (set_flag & VK_SET_BS) {
315 Bs[id] = (u_char) value;
316 } else if (set_flag & VK_SET_BATCH) {
317 Batch[id] = (u_char) value;
318 }
319 }
320
321
322 /*
323 * vk_flush: flushes the characters that are still pending
324 * in the FSM. If "outchars" is non-zero, the pending
325 * characters are stored there.
326 */
327 void FAR PASCAL
328 #ifdef ANSI_C
vk_flush(Vk_Fsm id,u_char FAR * outchars,int FAR * outcount)329 vk_flush(Vk_Fsm id, u_char FAR *outchars, int FAR *outcount)
330 #else
331 vk_flush(id, outchars, outcount)
332 Vk_Fsm id;
333 u_char FAR *outchars;
334 int FAR *outcount;
335 #endif
336 {
337 char dumbuf[3];
338 int dumcount;
339
340 /* do two com's and throw away the com itself */
341 if (outchars) {
342 vk_step(id, Com[id], outchars, outcount);
343 } else {
344 vk_step(id, Com[id], (u_char FAR *)dumbuf, (int FAR *)&dumcount);
345 }
346 vk_step(id, Com[id], (u_char FAR *)dumbuf, (int FAR *) &dumcount);
347 }
348
349
350 /*
351 * vk_end: vk_flush() the characters that are still pending
352 * in the FSM, and frees up the FSM for another call.
353 */
354 void FAR PASCAL
355 #ifdef ANSI_C
vk_end(Vk_Fsm id,u_char FAR * outchars,int FAR * outcount)356 vk_end(Vk_Fsm id, u_char FAR *outchars, int FAR *outcount)
357 #else
358 vk_end(id, outchars, outcount)
359 Vk_Fsm id;
360 u_char FAR *outchars;
361 int FAR *outcount;
362 #endif
363 {
364 vk_flush(id, outchars, outcount);
365 Fsm_in_use[id] = 0; /* free up the FSM */
366 }
367
368
369 #ifndef _WINDOWS /* code not good enough for Windows! */
370 /*
371 * vk_parse_args: parse the standard arguments that are common
372 * to all vnkeys applications:
373 *
374 * -com [char|\octal]: the compose character
375 * -bs [char|\octal]: the backspace character
376 *
377 * The value of ac is decreased by the number
378 * of arguments actually parsed. The vector *av[]
379 * is also modified so that only unparsed arguments remain.
380 *
381 */
382 void FAR PASCAL
383 #ifdef ANSI_C
vk_parse_args(int FAR * ac,char FAR * av[],u_char FAR * com,u_char FAR * bs)384 vk_parse_args(int FAR *ac, char FAR *av[], u_char FAR *com, u_char FAR *bs)
385 #else
386 vk_parse_args(ac, av, com, bs)
387 int FAR *ac;
388 char FAR *av[];
389 u_char FAR *com;
390 u_char FAR *bs;
391 #endif
392 {
393 int i, j;
394 char FAR **s;
395
396
397 for (i = 1, s = (char FAR **)(av+1); i < *ac; i++, s++) {
398 #ifdef _MSDOS
399 if (strcmp("-com", *s) == 0 || strcmp("/com", *s) == 0) {
400 #else
401 if (strcmp("-com", *s) == 0) {
402 #endif
403 *s++ = 0; i++;
404 if (*s && **s == '\\')
405 (void) sscanf("*s", "\\%o", com);
406 else if (*s) *com = **s;
407 else fprintf(stderr, "usage: -com [char|\\octal]\n");
408 *s = 0;
409 #ifdef _MSDOS
410 } else if (strcmp("-bs", *s) == 0 || strcmp("/bs", *s) == 0) {
411 #else
412 } else if (strcmp("-bs", *s) == 0) {
413 #endif
414 *s++ = 0; i++;
415 if (*s && **s == '\\') sscanf(*s, "\\%o", bs);
416 else if (*s) *bs = **s;
417 else fprintf(stderr, "usage: -bs [char|\\octal]\n");
418 *s = 0;
419 }
420 }
421
422 /* reorganize the *av[] vector */
423 for (i = j = 0, s = av; i < *ac; i++, av++) {
424 if (*av) {
425 *s++ = *av;
426 j++;
427 }
428 }
429 *s = 0;
430 *ac = j;
431 }
432 #endif /* !_WINDOWS */
433
434
435 #ifdef VK_DEBUG
436 /* debugging only */
437 static void
print_state(st)438 print_state(st)
439 Vk_State st;
440 {
441 printf("Current state = ");
442 switch(st) {
443 case VK_ST_VIETNAMESE:
444 printf("VK_ST_VIETNAMESE");
445 break;
446 case VK_ST_ENGLISH:
447 printf("VK_ST_ENGLISH");
448 break;
449 case VK_ST_ESC_VIETMY:
450 printf("VK_ST_VIETMY");
451 break;
452 case VK_ST_ESC_LITERAL:
453 printf("VK_ST_ESC_LITERAL");
454 break;
455 case VK_ST_LITERAL:
456 printf("VK_ST_LITERAL");
457 break;
458 default:
459 printf("VK_ST_unknown");
460 break;
461 }
462 }
463 #endif
464