/* vi: set sw=4 ts=4: */ /* * CCE - Console Chinese Environment - * Copyright (C) 1998-2003 Rui He (rhe@3eti.com) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE TERRENCE R. LAMBERT BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_SYS_MMAN_H #include #endif #ifdef HAVE_NETINET_IN_H #include #endif #define SUPPORT_ASSOCIATE #define USE_MMAP unsigned char SelTab[MAX_SELECT_KEYS][MAX_PHRASE_LENGTH+1]; int CurSelNum=0; /* Current Total Selection Number */ int EnableKeyPrompt = 1; /* global option, default is disabled */ unsigned char KeyPrompt[MAX_SELECT_KEYS][MAX_KEY_LENGTH+1]; int InpKey[MAX_INPUT_LENGTH]; int InputCount,InputMatch, StartIndex,EndIndex; int NextPageIndex,CurrentPageIndex,MultiPageMode; int SavedPageIndex[MAX_PAGE_INDEX]; int CurrentPage; /* 0-15 */ int IsDirectSelect; static KEYCODE KeyCode; static unsigned int keys_mask[2][19] = { { /* key_len == 5, 12 keys (6 keys per integer) */ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x3E000000, 0x3FF00000, 0x3FFF8000, 0x3FFFFC00, 0x3FFFFFE0, 0x3FFFFFFF, 0x3FFFFFFF, 0x3FFFFFFF, 0x3FFFFFFF, 0x3FFFFFFF, 0x3FFFFFFF, 0x3FFFFFFF }, { /* key_len == 6, 10 keys (5 keys per integer) */ 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x3F000000, 0x3FFC0000, 0x3FFFF000, 0x3FFFFFC0, 0x3FFFFFFF, 0x3FFFFFFF, 0x3FFFFFFF, 0x3FFFFFFF, 0x3FFFFFFF, 0x3FFFFFFF, 0x3FFFFFFF, 0x3FFFFFFF } }; #define FULL_KEY_MASK (0x3FFFFFFF) static inline unsigned char *GET_ITEM(int index) { return (((unsigned char *)cur_table->item) + index * cur_table->ItemBytes); } static void CalculateKeys(KEYCODE *kc, int count) { int len, i, keynum; unsigned int m; int wc1 = 0, wc2 = 0; kc->KVal1 = kc->KVal2 = kc->WMask1 = kc->WMask2 = 0; kc->WPreMask1 = kc->WPreMask2 = 0; len = cur_table->key_len; keynum = 32/len; m = (len == 5 ? 0x1F : 0x3F); for(i = 0; i < keynum; i++) { kc->KVal1 <<= len; kc->KVal1 |= InpKey[i]; kc->KVal2 <<= len; kc->KVal2 |= InpKey[i+keynum]; if (cur_table->wildcard > 0) { kc->WMask1 <<= len; kc->WMask2 <<= len; if (InpKey[i] < cur_table->wildcard) kc->WMask1 |= m; else if (wc1 == 0) wc1 = i+1; if (InpKey[i+keynum] < cur_table->wildcard) kc->WMask2 |= m; else if (wc2 == 0) wc2 = i+1+keynum; } } if (wc1 == 0) wc1 = wc2; /* now wc1 is the position of the 1st wildcard */ if (wc1 == 0) /* no wildcard in input keys */ { kc->WMask1 = kc->WMask2 = FULL_KEY_MASK; kc->WPreMask1 = kc->WPreMask2 = FULL_KEY_MASK; } else { kc->WPreMask1 = keys_mask[len - 5] [ keynum + wc1 - 1 ]; kc->WPreMask2 = keys_mask[len - 5] [ wc1 - 1 ]; kc->KVal1 &= kc->WMask1; kc->KVal2 &= kc->WMask2; } kc->LMask1 = keys_mask[len - 5] [ keynum + count ]; kc->LMask2 = (cur_table->ItemBytes < 12 ? 0x0 : keys_mask[len - 5][ count ] ); } static int TestMatchKeys(ITEM *item, int len, KEYCODE *kc) { u_int key1, key2, val1; key1 = item->key[0]; if (cur_table->prefix_match_enable) key1 &= kc->LMask1; if ((key1 & kc->WMask1) == kc->KVal1) /* first match */ { if (cur_table->ItemBytes <= 8) return 1; /* only one integer for keys, matched! */ key2 = item->key[1]; if (cur_table->prefix_match_enable) key2 &= kc->LMask2; if ((key2 & kc->WMask2) == kc->KVal2) return 1; /* matched! */ } key1 &= kc->WPreMask1; val1 = kc->KVal1 & kc->WPreMask1; if (key1 > val1) return -1; /* not-matched, more matches not possible */ return 0; /* not-matched, more matches possible */ } #define MMAP_SIZE(table) ((table)->ItemBytes * (table)->TotalEntries + sizeof(hz_input_table)) int MmapInputTable(hz_input_table *table) { unsigned char *p; if (!table || !table->TableFile) return -1; if (table->item) return 0; /* already mapped */ p = (unsigned char *)mmap(0, MMAP_SIZE(table), PROT_READ, MAP_SHARED, fileno(table->TableFile), 0); /* sizeof(hz_input_table)); */ if (p == MAP_FAILED) { error("Failed to mmap input table size %d, errno %d!\r\n", MMAP_SIZE(table), errno); return -1; } table->item = (ITEM *)(p + sizeof(hz_input_table)); #ifdef DEBUG message("Mmapped input table size %d at address %p\r\n", MMAP_SIZE(table), p); #endif return 0; } hz_input_table* LoadInputMethod(const char *filename) { int i; FILE *fd; char fname[CCE_PATHNAME_MAX+1]; hz_input_table *table; unsigned char *Shift_ASCII = " !\"#$%&'()*+,-./)!@#$%^&*(:;<=>?" "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`ABCDEFGHIJKLMNOPQRSTUVWXYZ{|}~ "; safe_strncpy(fname, filename, sizeof(fname)-5); /* reserved some space for .usr */ if (!(fd = fopen(fname, R_CNTRL))) { safe_strncpy(fname, CCELIB "/", sizeof(fname)-5); if (strlen(fname) + strlen(filename) > sizeof(fname)-5) return NULL; /* buffer overrun */ strcat(fname, filename); fd = fopen( fname, R_CNTRL); } if (fd == NULL) return NULL; table = malloc(sizeof(hz_input_table)); if (table == NULL) { error("Out of memory in LoadInputMethod"); fclose(fd); return NULL; } memset(table, 0, sizeof(hz_input_table)); if (fread(table, sizeof(hz_input_table), 1, fd) != 1 || strcmp(INPUT_MAGIC_NUMBER, table->magic_number) /* || table->PhraseBegin < sizeof(hz_input_table) || fseek(fd, table->PhraseBegin + table->PhraseFileSize, SEEK_SET) || fseek(fd, sizeof(hz_input_table), SEEK_SET) */ ) /* go back to original location */ { warn("Cannot load input method %s\n", filename); fclose(fd); free(table); return NULL; } table->TableFile = fd; /* Mmap need this one, also pinyin's init */ table->InputSelectKeyConflict = table->FirstInputSelectKeyConflict = 0; for(i = 0; i < table->MaxDupSel; i++) { int keymap; table->Shift_SelKey[i] = Shift_ASCII[(int)table->selkey[i]]; keymap = table->KeyMap[(int)table->selkey[i]]; if (keymap > 0) { table->InputSelectKeyConflict = 1; if (table->KeyIndex[keymap+1] - table->KeyIndex[keymap] > 0) table->FirstInputSelectKeyConflict = 1; /* first key conflict 1-0 is used as first key */ } } if (table->InputType == INPUT_TYPE_INTCODE) return Init_IntCodeInput(table, lInfo.sysCoding); if (table->InputType == INPUT_TYPE_PINYIN) return Init_PinyinInput(table, fname, lInfo.sysCoding); if (table->InputType == INPUT_TYPE_ZHUYIN) return Init_ZhuyinInput(table, fname, lInfo.sysCoding); if (table->InputType != INPUT_TYPE_TABLE) { error("Unknown input method type %d for %s!\n", table->InputType, filename); fclose(fd); free(table); return NULL; } if (table->encoding == CODE_GB && lInfo.sysCoding == CODE_GBK) table->encoding = CODE_GBK; if (table->encoding >= CODE_NUM || fDRegs[Coding2FontRegDB[(int)table->encoding]].fd < 0) { warn("No suitable font for input method %s\n", filename); fclose(fd); free(table); return NULL; } if (table->encoding == lInfo.sysCoding || fDRegs[Coding2FontRegDB[(int)table->encoding]].fi != MAP_FAILED) { table->FontMapped = 1; /* already mapped! */ } #ifdef DEBUG fprintf(stderr, "Now loading %s, keylen is %d encoding is 0x%x TotalEntry=%d ItemBytes=%d\n", filename, table->key_len, table->encoding, table->TotalEntries, table->ItemBytes); #endif #ifdef USE_MMAP #else table->item = (ITEM *)malloc(table->ItemBytes * table->TotalEntries); if ( table->item == NULL ) { error("Gosh, cannot malloc enough memory\n"); free(table); return NULL; } fread(table->item, table->ItemBytes, table->TotalEntries, fd ); #endif table->HZFilter = Table_HZFilter; table->RefreshInput = NULL; table->InputAreaX = cal_input_xpos(table); table->InputAreaWidth = MAX_INPUT_LENGTH; /* 15 */ table->SelAreaX = table->InputAreaX + table->InputAreaWidth + 1; table->SelAreaWidth = dispInfo.tx_avail - table->SelAreaX - 1 - dispInfo.show_terminal * WINDOW_TERMINAL_STR_WIDTH; /* 18 or 0 */ return table; } void UnloadInputMethod(int i) { if (input_table[i] == NULL) return; if (i == current_method) { if (IsHanziInput) ToggleInputMethod(); current_method = 0; } switch(input_table[i]->InputType) { case INPUT_TYPE_PINYIN: PinyinInputCleanup(input_table[i]); break; case INPUT_TYPE_INTCODE: case INPUT_TYPE_TABLE: if (input_table[i]->AssocFile) fclose(input_table[i]->AssocFile); if (input_table[i]->TableFile) /* for example, pinyin and intcode has NULL */ fclose(input_table[i]->TableFile); #ifdef USE_MMAP if (input_table[i]->item && input_table[i]->item != (ITEM *)-1) { #ifdef DEBUG message("Munmapped input table size %d at address %p\r\n", MMAP_SIZE(input_table[i]), ((u_char *)input_table[i]->item)-sizeof(hz_input_table)); #endif munmap(((u_char *)input_table[i]->item)-sizeof(hz_input_table), MMAP_SIZE(input_table[i])); input_table[i]->item = NULL; } #else #endif break; } free(input_table[i]); input_table[i] = NULL; } void ResetInput(int ClearInput, int ClearSelect) { memset( InpKey, 0, sizeof(InpKey) ); memset( SelTab, 0, sizeof(SelTab) ); StartIndex = EndIndex = MultiPageMode = NextPageIndex = CurrentPageIndex = 0; CurSelNum = InputCount = InputMatch = 0; IsAssociateMode = 0; /* lian xiang */ IsDirectSelect = 0; if (ClearInput) ClearInputArea(); if (ClearSelect) ClearSelectionArea(); } void DisplayInput(void) { int i,CurPos = cur_table->InputAreaX; unsigned char c1, c2; int EndPos = cur_table->InputAreaWidth + cur_table->InputAreaX; for( i = 0; i < cur_table->InputAreaWidth; i++) { if (i == InputMatch && InputCount > InputMatch && i != 0) { pVideoInfo->input_sput(CurPos++, '-' /* ' ' */, INPUT_FGCOLOR,INPUT_BGCOLOR); } if (i < InputCount) { c1 = input_table[current_method]->KeyName[InpKey[i]][0]; c2 = input_table[current_method]->KeyName[InpKey[i]][1]; if (c1 >= 0x80 && c2 != 0) /* >= 0x80, should be valid for all Chinese/Japanese/Korean */ { pVideoInfo->input_wput(cur_table->encoding, CurPos,c1, c2, INPUT_FGCOLOR,INPUT_BGCOLOR); CurPos += 2; } else { if (c1) pVideoInfo->input_sput(CurPos++, c1, INPUT_FGCOLOR,INPUT_BGCOLOR); if (c2) pVideoInfo->input_sput(CurPos++, c2, INPUT_FGCOLOR,INPUT_BGCOLOR); } } else pVideoInfo->input_sput(CurPos++, ' ', INPUT_FGCOLOR,INPUT_BGCOLOR); /* output a space */ if (CurPos >= EndPos) break; } } void DisplaySelection(void) { int i, len; int CurPos = cur_table->SelAreaX; int EndPos = cur_table->SelAreaX + cur_table->SelAreaWidth; if (CurSelNum > 0) /* we do have something */ { if (MultiPageMode && CurrentPageIndex != StartIndex) /* not first page */ { InputAreaOutput(cur_table->encoding, CurPos, "< ", INPUT_FGCOLOR,INPUT_BGCOLOR); CurPos += 2; } if (!IsDirectSelect) { InputAreaOutput(cur_table->encoding, CurPos, "Shift-", INPUT_FGCOLOR, INPUT_BGCOLOR); CurPos += 6; /* length */ } for( i = 0; i < CurSelNum; i++ ) { if ( !SelTab[i][0] ) { continue; /* just in case, if an empty string is put into SelTab, just skip to next one */ } len = strlen(SelTab[i]); if (CurPos+len+1+MultiPageMode >= EndPos) break; { pVideoInfo->input_sput(CurPos++, cur_table->selkey[i], INPUT_FGCOLOR,INPUT_BGCOLOR); } InputAreaOutput(IsAssociateMode ? lInfo.sysCoding /* cur_table->AscEncoding */: cur_table->encoding, CurPos,SelTab[i],INPUT_FGCOLOR,INPUT_BGCOLOR); CurPos += len; pVideoInfo->input_sput(CurPos++, ' ', INPUT_FGCOLOR,INPUT_BGCOLOR); } if ( MultiPageMode && NextPageIndex != StartIndex) /* not last page */ InputAreaOutput(cur_table->encoding, CurPos++, ">", INPUT_FGCOLOR,INPUT_BGCOLOR); } for(i = CurPos; i < EndPos; i++) pVideoInfo->input_sput(i, ' ', INPUT_FGCOLOR,INPUT_BGCOLOR); } void OutputSelection(int inEncoding, int tty_fd, unsigned char *p ) { int i, len = strlen(p); unsigned char LastChar[2]; if (inEncoding != lInfo.sysCoding) { for(i = 0; i < len ; i += 2) /* only output even bytes */ { ConvertEncoding_Char(/* cur_table->encoding */ inEncoding, lInfo.sysCoding, p[i], p[i+1], p+i, p+i+1); } } LastChar[0] = p[len-2]; LastChar[1] = p[len-1]; /* save it, otherwise ResetInput() will clear it */ OutputCJKStr(tty_fd, p, len); #if 0 /* def DEBUG */ { int i; fprintf(stderr, "Now in hzinput.c's OutputSelection, tty_fd=%d, String="); for(i = 0; i < len; i++) fprintf(stderr, "%02X", p[i]); fprintf(stderr, "\n"); } #endif if (InputCount <= InputMatch) /* All Match */ { ResetInput(1,1); #ifdef SUPPORT_ASSOCIATE if (IsAssocEnable && /* cur_table->EnableAssociate && */ cur_table->AssocFile) FindAssociateKeyAndFill(LastChar); /* using the input method's encoding to find */ #endif } else { int Unmatched = InputCount - InputMatch, nMatch = InputMatch, i; int save_InpKey[MAX_INPUT_LENGTH]; for(i = 0; i < Unmatched; i++) save_InpKey[i] = InpKey[nMatch+i]; ResetInput(1, 1); /* reset everything, including InpKey */ for(i = 0; i < Unmatched; i++) /* feed the additional keys */ { InpKey[InputCount++] = save_InpKey[i]; if (InputCount <= InputMatch+1) FindMatchKeyAndFill(); } DisplayInput(); DisplaySelection(); } } int CheckPhraseEncoding(int currentEncoding, int targetEncoding, unsigned char *buf) { int i, len; len = strlen(buf); if (len < 2 || len % 2 == 1) return -1; /* len should be even */ for(i = 0; i < len ; i += 2) { if (ConvertEncoding_Char(currentEncoding, targetEncoding, buf[i], buf[i+1], NULL, NULL) == -1) { /* failed to convert it to target encoding or currentEncoding error */ return -1; } } return 0; } #define GET_OFFSET(p) (((u_int)(p.Phrase[0])<<24) | ((u_int)(p.Phrase[1])<<16) | \ ((u_int)(p.Phrase[2])<<8) | ((u_int)(p.Phrase[3]))) static int GetSelection(ITEM *item, int SelTabIdx, int IdxEnd, int nMatch) { unsigned char buf[MAX_PHRASE_LENGTH+1]; int i, len; unsigned int offset; if (item->phr.Phrase[0] < 0x80) /* 0-0x7FFFFFFF, phrase offset */ { offset = GET_OFFSET(item->phr); if (fseek(cur_table->TableFile, cur_table->PhraseBegin + offset, SEEK_SET) || fread(buf, 1, MAX_PHRASE_LENGTH, cur_table->TableFile) < 2) return -1; buf[MAX_PHRASE_LENGTH] = '\0'; /* just in case */ } else /* Two 2 or 4 bytes Char/Phrase */ { if (item->phr.Phrase[2] >= 0x80) /* a valid char? */ len = 4; else len = 2; memcpy( buf, item->phr.Phrase, len); buf[len] = '\0'; } if (CheckPhraseEncoding(cur_table->encoding, lInfo.sysCoding, buf)) return -1; if (IdxEnd == 0) /* filling forward */ { for(i = 0; i < SelTabIdx; i++) if (strcmp(SelTab[i], buf) == 0) return -1; } else /* filling backward */ { for(i = SelTabIdx+1; i < IdxEnd; i++) if (strcmp(SelTab[i], buf) == 0) return -1; } strcpy(SelTab[SelTabIdx], buf); /* it's safe here */ if (EnableKeyPrompt && cur_table->key_prompt) { for(i = nMatch+1; i <= cur_table->MaxPress; i++) { int key, keynum = 32 / cur_table->key_len; /* how many keys per integer */ if (i > keynum) /* using key[1] */ key = item->key[1] >> (cur_table->key_len * (2*keynum - i)) ; else key = item->key[0] >> (cur_table->key_len * (keynum-i)); KeyPrompt[SelTabIdx][i-nMatch-1] = cur_table->KeyName[key & 0x3F][2]; } KeyPrompt[SelTabIdx][i-nMatch-1] = '\0'; } return 0; } static int TestInputSelectKeyConflict(int nCount) { if (!cur_table->InputSelectKeyConflict || nCount >= cur_table->MaxPress) { return 0; } else { int i, j; for(i = 0; i < cur_table->MaxDupSel; i++) { unsigned int k0, k1; KEYCODE kc; unsigned char *item; InpKey[nCount] = cur_table->KeyMap[cur_table->selkey[i]]; if (InpKey[nCount] == 0) continue; /* select key not conflict with input key */ CalculateKeys(&kc, nCount+1); InpKey[nCount] = 0; /* it's important to restore it */ item = GET_ITEM(StartIndex); for(j = StartIndex; j < EndIndex; j++, item += cur_table->ItemBytes) { k0 = ((ITEM *)item)->key[0] & kc.LMask1 & kc.WMask1; k1 = ((ITEM *)item)->key[1] & kc.LMask2 & kc.WMask2; /* k1=0 if ItemBytes <=8 */ if (k0 > kc.KVal1) break; /* bigger, no match */ if (k0 < kc.KVal1) continue; /* smaller, try next */ if (k1 < kc.KVal2) continue; /* now k0==v0, k1=v1 */ } } } return 0; } int FindMatchKeyAndFill(void) { unsigned int key1 = 0, key2 = 0; KEYCODE tmpKC; int tmpStartIndex, tmpEndIndex; unsigned char *item; static int LastStartIndex[MAX_INPUT_LENGTH]; CalculateKeys(&tmpKC, InputCount); /* tmpKeyVal, tmpWmask); */ if (InputCount == 1) tmpStartIndex = cur_table->KeyIndex[InpKey[0]]; else tmpStartIndex = LastStartIndex[InputCount-1]; tmpEndIndex = cur_table->KeyIndex[InpKey[0]+1]; item = GET_ITEM(tmpStartIndex); for (; tmpStartIndex < tmpEndIndex; tmpStartIndex++, item += cur_table->ItemBytes) { int middleIndex; unsigned char *middleItem; key1 = ((ITEM *)item)->key[0] & tmpKC.LMask1 & tmpKC.WMask1; if (tmpKC.WMask1 >= FULL_KEY_MASK) /* no wildcard, 30 bits */ { if (key1 > tmpKC.KVal1) break; /* no match */ middleIndex = (tmpStartIndex + tmpEndIndex)/2; middleItem = GET_ITEM(middleIndex); if (( ((ITEM *)middleItem)->key[0] & tmpKC.LMask1 & tmpKC.WMask1) < tmpKC.KVal1) { tmpStartIndex = middleIndex; item = middleItem; continue; } if (key1 < tmpKC.KVal1) continue; } else { if (key1 < tmpKC.KVal1) continue; if ((key1 & tmpKC.WPreMask1) > (tmpKC.KVal1 & tmpKC.WPreMask1)) break; /* not-matched, more matches not possible */ if (key1 > tmpKC.KVal1) continue; } key2 = ((ITEM *)item)->key[1] & tmpKC.LMask2 & tmpKC.WMask2; if (tmpKC.WMask2 >= FULL_KEY_MASK) /* 30 bits no wildcard */ { if (key2 < tmpKC.KVal2) continue; break; /* found a match or no match */ } else /* with wildcard */ { if (key2 != tmpKC.KVal2) continue; } } LastStartIndex[InputCount] = tmpStartIndex; if (key1 == tmpKC.KVal1 && key2 == tmpKC.KVal2) InputMatch = InputCount; /* record the current match pointer (prefix-match)*/ if (FillMatchCharsForward(1, InputCount, &tmpStartIndex, tmpEndIndex, &tmpKC) > 0 || !cur_table->prefix_match_enable) { StartIndex = tmpStartIndex; EndIndex = tmpEndIndex; /* These values are valid and won't be restored */ KeyCode = tmpKC; IsDirectSelect = !TestInputSelectKeyConflict(InputCount); return CurSelNum; } else /* no further matches found */ { if (CurSelNum > 0) IsDirectSelect = 1; } return 0; } int FillMatchCharsForward(int FirstFill, int nCount, int *CurIndex, int CurEndIndex, KEYCODE *kc) { int tmpSelNum = 0, CurLen = 0; int index = *CurIndex; int tmpStart = -1; int match; unsigned char *item; item = GET_ITEM(index); while(tmpSelNum < cur_table->MaxDupSel && index < CurEndIndex) { match = TestMatchKeys((ITEM *)item, nCount, kc); if (match == 1) { if (!GetSelection((ITEM *)item, tmpSelNum, 0, nCount)) { CurLen += strlen(SelTab[tmpSelNum]) + 2; if (tmpStart == -1) tmpStart = index; /* record the actual start key */ if (CurLen > cur_table->SelAreaWidth-3) break; /* -3 for "< >" */ tmpSelNum++; } } index++; /* try the next key */ item += cur_table->ItemBytes; if (match == -1) break; /* should we reset CurEndIndex */ } if (tmpSelNum == 0) /* no new match found */ { if (!FirstFill) { EndIndex = *CurIndex; /* starting from *CurIndex, no more match found */ if (NextPageIndex != StartIndex) NextPageIndex = StartIndex; if (CurrentPageIndex == NextPageIndex) MultiPageMode = 0; } if (cur_table->prefix_match_enable) return 0; } CurSelNum = tmpSelNum; while (tmpSelNum < MAX_SELECT_KEYS) SelTab[tmpSelNum++][0] = '\0'; /* zero out the unused area */ if (FirstFill) { *CurIndex = CurrentPageIndex = NextPageIndex = tmpStart; MultiPageMode = 0; CurrentPage = 0; SavedPageIndex[0] = tmpStart; } else if (CurrentPage < MAX_PAGE_INDEX) SavedPageIndex[CurrentPage] = *CurIndex; if ( index < CurEndIndex && TestMatchKeys((ITEM *)item, nCount, kc) >= 0) { NextPageIndex = index; MultiPageMode = 1; } else if (MultiPageMode) { NextPageIndex = StartIndex; /* rotate selection */ if (CurrentPageIndex == NextPageIndex) MultiPageMode = 0; } return CurSelNum; } int FillMatchCharsBackward(int CurIndex, int nCount, KEYCODE *kc) { int tmpSelNum, CurLen = 0; unsigned char *item; int match; int i, index = CurIndex-1; i = cur_table->MaxDupSel - 1; /* fill this one first */ item = GET_ITEM(index); while( i >= 0 && index >= StartIndex) { match = TestMatchKeys((ITEM *)item, nCount, kc); if (match == 1) { if (!GetSelection((ITEM *)item, i, cur_table->MaxDupSel, nCount)) { CurLen += strlen(SelTab[i]) + 2; if (CurLen >= cur_table->SelAreaWidth-3) break; i--; } } index--; /* try the next key */ item -= cur_table->ItemBytes; } tmpSelNum = cur_table->MaxDupSel - i - 1; /* how many do we get? */ if (tmpSelNum == 0) /* no new match found */ return 0; /* keep the original selection */ if (i >= 0) { while(++i < cur_table->MaxDupSel) strcpy(SelTab[i-cur_table->MaxDupSel+tmpSelNum], SelTab[i]); } CurSelNum = tmpSelNum; while (tmpSelNum < MAX_SELECT_KEYS) SelTab[tmpSelNum++][0] = '\0'; /* zero out the unused area */ MultiPageMode = 1; NextPageIndex = CurIndex; CurrentPageIndex = index+1; return CurSelNum; } static int ChangeDisplayPage_Filter(unsigned char key) { if (index(cur_table->pageprevkey, key)) { if (CurrentPage != 0) { if (--CurrentPage < MAX_PAGE_INDEX) { CurrentPageIndex = SavedPageIndex[CurrentPage]; FillMatchCharsForward(0, InputMatch, &CurrentPageIndex, EndIndex, &KeyCode); } else FillMatchCharsBackward(CurrentPageIndex, InputMatch, &KeyCode); DisplaySelection(); } return 1; } else if (index(cur_table->pagenextkey, key) ) { if (NextPageIndex <= CurrentPageIndex) /* last page, need to rorate */ CurrentPage = 0; else CurrentPage++; CurrentPageIndex = NextPageIndex; FillMatchCharsForward(0, InputMatch, &CurrentPageIndex, EndIndex, &KeyCode); DisplaySelection(); return 1; } return 0; /* not processed */ } int Table_HZFilter(int tty_fd, unsigned char key) { int inkey = 0,SelKeyIndex; char *SelKeyStr; char *is_sel_key = (char*)0; SelKeyStr = ( IsDirectSelect ? cur_table->selkey : cur_table->Shift_SelKey); if (CurSelNum > 0 && (is_sel_key = strchr( SelKeyStr, key))) /* not NULL, we fount it */ { SelKeyIndex = is_sel_key - SelKeyStr; if (SelKeyIndex < CurSelNum && SelTab[SelKeyIndex][0]) { OutputSelection(cur_table->encoding, tty_fd, SelTab[SelKeyIndex]); return 1; /* it's a valid select key, output and return */ } } inkey = cur_table->KeyMap[key]; if (inkey > 0) /* valid input key? */ { if ( InputCount < (MAX_INPUT_LENGTH / cur_table->keyname_bytes) ) InpKey[InputCount++] = inkey; if ( cur_table->prefix_match_enable == 0 || InputCount - InputMatch <= 1) { FindMatchKeyAndFill(); if (/* InputCount */ InputMatch >= cur_table->MaxPress && CurSelNum == 1 && cur_table->auto_select) { /* left only one selection */ OutputSelection(cur_table->encoding, tty_fd, SelTab[0]); return 1; } DisplaySelection(); /* maybe from Shift- to Direct State or some new matches */ #if 0 if (InputCount > InputMatch) /* new key has no match */ { if (ChangeDisplayPage_Filter(key) || cur_table->KeyIndex[inkey+1] - cur_table->KeyIndex[inkey] <= 0) { InpKey[--InputCount] = '\0'; return 1; /* should we output this key? */ } } #endif } /* if */ DisplayInput(); return 1; } switch ( key ) { case '\010': /* BackSpace Ctrl+H */ case '\177': /* BackSpace 127 DEL */ if ( InputCount > 0 /* || IsAssociateMode*/ ) { if (InputCount > 0) InpKey[--InputCount] = '\0'; if (InputCount == 0) ResetInput(0,1); else if (InputCount < InputMatch) { InputMatch = InputCount; /* fix the InputMatch now */ FindMatchKeyAndFill(); DisplaySelection(); } DisplayInput(); return 1; } break; case '\033': /* ESCAPE */ if (InputCount > 0 ) { ResetInput(1,1); return 1; } break; case ' ': /* should we use SPACE to do PageNext or SelectFirstItem? */ if (IsDirectSelect) /* maybe in Shift-Select State */ { } else { if (InputCount > 0) { IsDirectSelect = 1; /* switch state */ DisplaySelection(); /* refresh display */ return 1; } } break; } /* switch */ if (/* CurSelNum > 0 && */ MultiPageMode && ChangeDisplayPage_Filter(key)) return 1; if (CurSelNum > 0 && strchr(cur_table->selfirstkey, key) && SelTab[0][0] ) /* do we need to check IsDirectSelct? */ { OutputSelection(cur_table->encoding, tty_fd, SelTab[0]); return 1; } return 0; /* we didn't handle it */ }