#ifndef lint static char sccs_id[] = "%W% (TriChlor) %G% %U%"; #endif /* lint */ #ifdef _WINDOWS #include #include #endif /* _WINDOWS */ #include #include "portable.h" #include "vnkeys.h" #include "vncompos.h" #if VK_DEBUG /* NOT ifdef! */ static int Vk_debug = 0; #endif static Vk_Fsm Id[VK_MAX_FSMS]; static u_char Fsm_in_use[VK_MAX_FSMS]; static Vk_State State[VK_MAX_FSMS]; static Vk_State Prev_State[VK_MAX_FSMS]; static u_char Com[VK_MAX_FSMS]; /* composition announcer key */ static u_char Bs[VK_MAX_FSMS]; /* configurable backspace */ static u_char Batch[VK_MAX_FSMS]; /* batch or interactive, def = batch */ static u_char Prev_temp[VK_MAX_FSMS]; /* for EXPLICIT_COMPOSE state */ /* Make Co_data publicly available for diacritic manipulations! */ Co_Data FAR Co_data[VK_MAX_FSMS]; /* storage area for composition machinery */ #if VK_DEBUG static void print_state(); #endif #define output_ch(ch, outchars, outcount) \ (outchars)[(*(outcount))++] = (ch) /* * vk_init: Initialize the state machine in the given state "st", * using "com" as the * compose key, and "bs" as the backspace character to be * sent out when needed in immediate echo mode. * * Returns ID of FSM if successful, -1 if failed. */ Vk_Fsm FAR PASCAL #ifdef ANSI_C vk_init(Vk_State st, u_char com, u_char bs) #else vk_init(st, com, bs) Vk_State st; u_char com; u_char bs; #endif { Vk_Fsm id; for (id = 0; id < VK_MAX_FSMS; id++) { if (!Fsm_in_use[id]) break; } if (id == VK_MAX_FSMS) return(-1); Fsm_in_use[id] = 1; State[id] = st; Com[id] = com; Bs[id] = bs; Batch[id] = 0; /* by default it's interactive */ Prev_temp[id] = 0; /* in case it's initialized mid-stream */ #if VK_DEBUG if (Vk_debug) { print_state(st, State[id]); printf("Com[id] = %d, Bs[id] = %d\n", Com[id], Bs[id]); } #endif vncompose_init((Co_Data FAR *)&Co_data[id]); /* initialize the composition tables */ return(id); } /* * vk_step: Takes one input key "inkey" and operates the state machine * accordingly. The output, if any, is stored in the user-provided * array "outchars", with the number of characters output in * "outcount". * */ Vk_State FAR PASCAL #ifdef ANSI_C vk_step(Vk_Fsm id, u_char inkey, u_char FAR outchars[], int FAR *outcount) #else vk_step(id, inkey, outchars, outcount) Vk_Fsm id; u_char inkey; u_char FAR outchars[]; int FAR *outcount; #endif { u_char temp; /* for storate of composed character */ #ifdef _WINDOWS static #endif int fl_compose; /* flag: in compose sequence */ #ifdef _WINDOWS static #endif int fl_erase; /* flag: erase previous vowel */ #ifdef VK_DEBUG if (Vk_debug) { switch(inkey) { case '\003': /* ctrl-c */ return(State[id]); case '\011': /* ctrl-I */ if (Mode[id] == VK_MODE_DELAYED) Mode[id] = VK_MODE_IMMEDIATE; else Mode[id] = VK_MODE_DELAYED; printf("Mode = %d\n", Mode[id]); return(State[id]); } } #endif *outcount = 0; while (1) { switch(State[id]) { /***********************************************/ case VK_ST_LITERAL: if (inkey == Com[id]) { State[id] = VK_ST_ESC_LITERAL; } else { output_ch(inkey, outchars, outcount); } return(State[id]); /***********************************************/ case VK_ST_ESC_LITERAL: switch(inkey) { case VK_ESC_VIETNAMESE_1: case VK_ESC_VIETNAMESE_2: State[id] = VK_ST_VIETNAMESE; break; case VK_ESC_ENGLISH_1: case VK_ESC_ENGLISH_2: State[id] = VK_ST_ENGLISH; break; case VK_ESC_LITERAL_1: case VK_ESC_LITERAL_2: State[id] = VK_ST_LITERAL; break; default: State[id] = VK_ST_LITERAL; output_ch(Com[id], outchars, outcount); output_ch(inkey, outchars, outcount); break; } return(State[id]); /***********************************************/ case VK_ST_VIETNAMESE: if (inkey == Com[id]) { Prev_State[id] = VK_ST_VIETNAMESE; State[id] = VK_ST_ESC_VIETMY; } else { temp = vncompose(&Co_data[id], inkey, (int FAR *) &fl_compose, (int FAR *) &fl_erase); if (Batch[id] && fl_compose) { /* batch mode composition */ /* give it to EXPLICIT COMPOSITION, which delays the echo */ Prev_State[id] = VK_ST_VIETNAMESE; State[id] = VK_ST_EXPLICIT_COMPOSE; Prev_temp[id] = temp; /* save result since we're in delayed echo */ } else { /* interactive mode composition */ if (fl_erase) { output_ch(Bs[id], outchars, outcount); } output_ch(temp, outchars, outcount); } } return(State[id]); /***********************************************/ case VK_ST_ENGLISH: if (inkey == Com[id]) { Prev_State[id] = VK_ST_ENGLISH; State[id] = VK_ST_ESC_VIETMY; } else { output_ch(inkey, outchars, outcount); } return(State[id]); /***********************************************/ case VK_ST_ESC_VIETMY: switch(inkey) { case VK_ESC_VIETNAMESE_1: case VK_ESC_VIETNAMESE_2: State[id] = VK_ST_VIETNAMESE; return(State[id]); case VK_ESC_ENGLISH_1: case VK_ESC_ENGLISH_2: State[id] = VK_ST_ENGLISH; return(State[id]); case VK_ESC_LITERAL_1: case VK_ESC_LITERAL_2: State[id] = VK_ST_LITERAL; return(State[id]); default: /* we got x, so flush the compose machine first */ Prev_temp[id] = 0; (void) vncompose(&Co_data[id], (u_char) 0, (int FAR *) &fl_compose, (int FAR *) &fl_erase); if (inkey == Com[id]) { output_ch(Com[id], outchars, outcount); State[id] = Prev_State[id]; return(State[id]); } else { State[id] = VK_ST_EXPLICIT_COMPOSE; break; /* we DON'T return, go back to start composing on this key */ } } break; /***********************************************/ case VK_ST_EXPLICIT_COMPOSE: temp = vncompose(&Co_data[id], inkey, (int FAR *) &fl_compose, (int FAR *) &fl_erase); if (fl_compose) { /* composition sequence starting or continuing */ if (!fl_erase && Prev_temp[id]) { /* we just ended a composition sequence, e.g., \ae */ output_ch(Prev_temp[id], outchars, outcount); if (!Batch[id] && Prev_State[id] == VK_ST_VIETNAMESE) { /* we're not really in delayed echo */ output_ch(temp, outchars, outcount); State[id] = VK_ST_VIETNAMESE; } else { Prev_temp[id] = temp; /* save result since we're in delayed echo */ } } else { /* did not just end a composition sequence */ Prev_temp[id] = temp; /* save result since we're in delayed echo */ } } else { /* composition is finished */ State[id] = Prev_State[id]; /* Now, why was composition finished? */ if (fl_erase) { /* because it's completed, e.g., \e^' */ output_ch(temp, outchars, outcount); } else if (temp == Com[id]) { /* because it's ended by \, e.g., \e^\ */ output_ch(Prev_temp[id], outchars, outcount); State[id] = VK_ST_ESC_VIETMY; } else { /* because it's ended by something else, e.g., \m or \e^m */ if (Prev_temp[id] != 0) { output_ch(Prev_temp[id], outchars, outcount); } output_ch(temp, outchars, outcount); } } return(State[id]); } /* switch(St[id]) */ } /* forever */ } /* * vk_get: called when the user wishes to query the internal * state of the FSM. */ void FAR PASCAL #ifdef ANSI_C vk_get(Vk_Fsm id, u_int FAR *value, int get_flag) #else vk_get(id, value, get_flag) Vk_Fsm id; u_int FAR *value; int get_flag; #endif { if (get_flag & VK_GET_STATE) { *((Vk_State FAR *) value) = State[id]; } else if (get_flag & VK_GET_ESC) { *((u_char FAR *) value) = Com[id]; } else if (get_flag & VK_GET_BS) { *((u_char FAR *) value) = Bs[id]; } else if (get_flag & VK_GET_BATCH) { *((u_char FAR *) value) = Batch[id]; } } /* * vk_set: called when the user wishes to set the internal * state of the FSM. */ void FAR PASCAL #ifdef ANSI_C vk_set(Vk_Fsm id, u_int value, int set_flag) #else vk_set(id, value, set_flag) Vk_Fsm id; u_int value; int set_flag; #endif { if (set_flag & VK_SET_STATE) { State[id] = (Vk_State) value; vk_flush(id, 0, 0); /* flush the FSM whenever we set the state */ } else if (set_flag & VK_SET_ESC) { Com[id] = (u_char) value; } else if (set_flag & VK_SET_BS) { Bs[id] = (u_char) value; } else if (set_flag & VK_SET_BATCH) { Batch[id] = (u_char) value; } } /* * vk_flush: flushes the characters that are still pending * in the FSM. If "outchars" is non-zero, the pending * characters are stored there. */ void FAR PASCAL #ifdef ANSI_C vk_flush(Vk_Fsm id, u_char FAR *outchars, int FAR *outcount) #else vk_flush(id, outchars, outcount) Vk_Fsm id; u_char FAR *outchars; int FAR *outcount; #endif { char dumbuf[3]; int dumcount; /* do two com's and throw away the com itself */ if (outchars) { vk_step(id, Com[id], outchars, outcount); } else { vk_step(id, Com[id], (u_char FAR *)dumbuf, (int FAR *)&dumcount); } vk_step(id, Com[id], (u_char FAR *)dumbuf, (int FAR *) &dumcount); } /* * vk_end: vk_flush() the characters that are still pending * in the FSM, and frees up the FSM for another call. */ void FAR PASCAL #ifdef ANSI_C vk_end(Vk_Fsm id, u_char FAR *outchars, int FAR *outcount) #else vk_end(id, outchars, outcount) Vk_Fsm id; u_char FAR *outchars; int FAR *outcount; #endif { vk_flush(id, outchars, outcount); Fsm_in_use[id] = 0; /* free up the FSM */ } #ifndef _WINDOWS /* code not good enough for Windows! */ /* * vk_parse_args: parse the standard arguments that are common * to all vnkeys applications: * * -com [char|\octal]: the compose character * -bs [char|\octal]: the backspace character * * The value of ac is decreased by the number * of arguments actually parsed. The vector *av[] * is also modified so that only unparsed arguments remain. * */ void FAR PASCAL #ifdef ANSI_C vk_parse_args(int FAR *ac, char FAR *av[], u_char FAR *com, u_char FAR *bs) #else vk_parse_args(ac, av, com, bs) int FAR *ac; char FAR *av[]; u_char FAR *com; u_char FAR *bs; #endif { int i, j; char FAR **s; for (i = 1, s = (char FAR **)(av+1); i < *ac; i++, s++) { #ifdef _MSDOS if (strcmp("-com", *s) == 0 || strcmp("/com", *s) == 0) { #else if (strcmp("-com", *s) == 0) { #endif *s++ = 0; i++; if (*s && **s == '\\') (void) sscanf("*s", "\\%o", com); else if (*s) *com = **s; else fprintf(stderr, "usage: -com [char|\\octal]\n"); *s = 0; #ifdef _MSDOS } else if (strcmp("-bs", *s) == 0 || strcmp("/bs", *s) == 0) { #else } else if (strcmp("-bs", *s) == 0) { #endif *s++ = 0; i++; if (*s && **s == '\\') sscanf(*s, "\\%o", bs); else if (*s) *bs = **s; else fprintf(stderr, "usage: -bs [char|\\octal]\n"); *s = 0; } } /* reorganize the *av[] vector */ for (i = j = 0, s = av; i < *ac; i++, av++) { if (*av) { *s++ = *av; j++; } } *s = 0; *ac = j; } #endif /* !_WINDOWS */ #ifdef VK_DEBUG /* debugging only */ static void print_state(st) Vk_State st; { printf("Current state = "); switch(st) { case VK_ST_VIETNAMESE: printf("VK_ST_VIETNAMESE"); break; case VK_ST_ENGLISH: printf("VK_ST_ENGLISH"); break; case VK_ST_ESC_VIETMY: printf("VK_ST_VIETMY"); break; case VK_ST_ESC_LITERAL: printf("VK_ST_ESC_LITERAL"); break; case VK_ST_LITERAL: printf("VK_ST_LITERAL"); break; default: printf("VK_ST_unknown"); break; } } #endif