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