// -*- coding:unix; mode:c++; tab-width:4; c-basic-offset:4; indent-tabs-mode:nil -*-
/* Unikey Vietnamese Input Method
* Copyright (C) 2000-2005 Pham Kim Long
* Contact:
* unikey@gmail.com
* UniKey project: http://unikey.org
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor
* Boston, MA 02110-1301, USA.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
#include "mactab.h"
#include "vnconv.h"
using namespace std;
#define UKMACRO_VERSION_UTF8 1
//---------------------------------------------------------------
void CMacroTable::init()
{
m_memSize = MACRO_MEM_SIZE;
m_count = 0;
m_occupied = 0;
}
//---------------------------------------------------------------
char *MacCompareStartMem;
#define STD_TO_LOWER(x) (((x) >= VnStdCharOffset && \
(x) < (VnStdCharOffset + TOTAL_ALPHA_VNCHARS) && \
!((x) & 1)) ? \
(x+1) : (x))
int macCompare(const void *p1, const void *p2)
{
StdVnChar *s1 = (StdVnChar *) ((char *)MacCompareStartMem + ((MacroDef *)p1)->keyOffset);
StdVnChar *s2 = (StdVnChar *) ((char *)MacCompareStartMem + ((MacroDef *)p2)->keyOffset);
int i;
StdVnChar ls1, ls2;
for (i=0; s1[i] != 0 && s2[i] != 0; i++) {
ls1 = STD_TO_LOWER(s1[i]);
ls2 = STD_TO_LOWER(s2[i]);
if (ls1 > ls2)
return 1;
if (ls1 < ls2)
return -1;
/*
if (s1[i] > s2[i])
return 1;
if (s1[i] < s2[i])
return -1;
*/
}
if (s1[i] == 0)
return (s2[i] == 0)? 0 : -1;
return 1;
}
//---------------------------------------------------------------
int macKeyCompare(const void *key, const void *ele)
{
StdVnChar *s1 = (StdVnChar *)key;
StdVnChar *s2 = (StdVnChar *) ((char *)MacCompareStartMem + ((MacroDef *)ele)->keyOffset);
StdVnChar ls1, ls2;
int i;
for (i=0; s1[i] != 0 && s2[i] != 0; i++) {
ls1 = STD_TO_LOWER(s1[i]);
ls2 = STD_TO_LOWER(s2[i]);
if (ls1 > ls2)
return 1;
if (ls1 < ls2)
return -1;
/*
if (s1[i] > s2[i])
return 1;
if (s1[i] < s2[i])
return -1;
*/
}
if (s1[i] == 0)
return (s2[i] == 0)? 0 : -1;
return 1;
}
//---------------------------------------------------------------
const StdVnChar *CMacroTable::lookup(StdVnChar *key)
{
MacCompareStartMem = m_macroMem;
MacroDef *p = (MacroDef *)bsearch(key, m_table, m_count, sizeof(MacroDef), macKeyCompare);
if (p)
return (StdVnChar *)(m_macroMem + p->textOffset);
return 0;
}
//----------------------------------------------------------------------------
// Read header, if it's present in the file. Get the version of the file
// If header is absent, go back to the beginning of file and set version to 0
// Return false if reading failed.
//
// Header format: ;[DO NOT DELETE THIS LINE]***version=n
//----------------------------------------------------------------------------
bool CMacroTable::readHeader(FILE *f, int & version)
{
char line[MAX_MACRO_LINE];
if (!fgets(line, sizeof(line), f)) {
if (feof(f)) {
fseek(f, 0, SEEK_SET);
version = 0;
return true;
}
return false;
}
//if BOM is available, skip it
char *p = line;
size_t len = strlen(line);
if (len >= 3 && (unsigned char)line[0] == 0xEF && (unsigned char)line[1] == 0xBB &&
(unsigned char)line[2] == 0xBF)
{
p += 3;
}
//read version number
p = strstr(p, "***");
if (p) {
p += 3;
//skip possible spaces
while (*p == ' ') p++;
if (sscanf(p, "version=%d", &version) == 1)
return true;
}
fseek(f, 0, SEEK_SET);
version = 0;
return true;
}
//----------------------------------------------------------------
void CMacroTable::writeHeader(FILE *f)
{
#if defined(WIN32)
fprintf(f, "\xEF\xBB\xBF;DO NOT DELETE THIS LINE*** version=%d ***\n", UKMACRO_VERSION_UTF8);
#else
fprintf(f, "DO NOT DELETE THIS LINE*** version=%d ***\n", UKMACRO_VERSION_UTF8);
#endif
}
//---------------------------------------------------------------
int CMacroTable::loadFromFile(const char *fname)
{
FILE *f;
#if defined(WIN32)
f = _tfopen(fname, _TEXT("rt"));
#else
f = fopen(fname, "r");
#endif
if (f == NULL)
return 0;
char line[MAX_MACRO_LINE];
size_t len;
resetContent();
//read possible header
int version;
if (!readHeader(f, version)) {
version = 0;
}
while (fgets(line, sizeof(line), f)) {
len = strlen(line);
if (len > 0 && line[len-1] == '\n')
line[len-1] = 0;
if (len > 1 && line[len-2] == '\r')
line[len-2] = 0;
if (version == UKMACRO_VERSION_UTF8)
addItem(line, CONV_CHARSET_UNIUTF8);
else
addItem(line, CONV_CHARSET_VIQR);
}
fclose(f);
MacCompareStartMem = m_macroMem;
qsort(m_table, m_count, sizeof(MacroDef), macCompare);
// Convert old version
if (version != UKMACRO_VERSION_UTF8) {
writeToFile(fname);
}
return 1;
}
//---------------------------------------------------------------
int CMacroTable::writeToFile(const char *fname)
{
FILE *f;
#if defined(WIN32)
f = _tfopen(fname, _TEXT("wt"));
#else
f = fopen(fname, "w");
#endif
return writeToFp(f);
}
int CMacroTable::writeToFp(FILE* f)
{
int ret;
int inLen, maxOutLen;
if (f == NULL)
return 0;
char line[MAX_MACRO_LINE*3]; //1 VnChar may need 3 chars in UTF8
char key[MAX_MACRO_KEY_LEN*3];
char text[MAX_MACRO_TEXT_LEN*3];
writeHeader(f);
UKBYTE *p;
for (int i=0; i < m_count; i++) {
p = (UKBYTE *)m_macroMem + m_table[i].keyOffset;
inLen = -1;
maxOutLen = sizeof(key);
ret = VnConvert(CONV_CHARSET_VNSTANDARD, CONV_CHARSET_UNIUTF8,
(UKBYTE *) p, (UKBYTE *)key,
&inLen, &maxOutLen);
if (ret != 0)
continue;
p = (UKBYTE *)m_macroMem + m_table[i].textOffset;
inLen = -1;
maxOutLen = sizeof(text);
ret = VnConvert(CONV_CHARSET_VNSTANDARD, CONV_CHARSET_UNIUTF8,
p, (UKBYTE *)text,
&inLen, &maxOutLen);
if (ret != 0)
continue;
if (i < m_count-1)
sprintf(line, "%s:%s\n", key, text);
else
sprintf(line, "%s:%s", key, text);
fputs(line, f);
}
fclose(f);
return 1;
}
//---------------------------------------------------------------
int CMacroTable::addItem(const void *key, const void *text, int charset)
{
int ret;
int inLen, maxOutLen;
int offset = m_occupied;
char *p = m_macroMem + offset;
if (m_count >= MAX_MACRO_ITEMS)
return -1;
m_table[m_count].keyOffset = offset;
// Convert macro key to VN standard
inLen = -1; //input is null-terminated
maxOutLen = MAX_MACRO_KEY_LEN * sizeof(StdVnChar);
if (maxOutLen + offset > m_memSize)
maxOutLen = m_memSize - offset;
ret = VnConvert(charset, CONV_CHARSET_VNSTANDARD,
(UKBYTE *)key, (UKBYTE *)p,
&inLen, &maxOutLen);
if (ret != 0)
return -1;
offset += maxOutLen;
p += maxOutLen;
//convert macro text to VN standard
m_table[m_count].textOffset = offset;
inLen = -1; //input is null-terminated
maxOutLen = MAX_MACRO_TEXT_LEN * sizeof(StdVnChar);
if (maxOutLen + offset > m_memSize)
maxOutLen = m_memSize - offset;
ret = VnConvert(charset, CONV_CHARSET_VNSTANDARD,
(UKBYTE *)text, (UKBYTE *)p,
&inLen, &maxOutLen);
if (ret != 0)
return -1;
m_occupied = offset + maxOutLen;
m_count++;
return (m_count-1);
}
//---------------------------------------------------------------
// add a new macro into the sorted macro table
// item format: key:text (key and text are separated by a colon)
//---------------------------------------------------------------
int CMacroTable::addItem(const char *item, int charset)
{
char key[MAX_MACRO_KEY_LEN];
// Parse the input item
char * pos = (char*)strchr(item, ':');
if (pos == NULL)
return -1;
int keyLen = (int)(pos - item);
if (keyLen > MAX_MACRO_KEY_LEN-1)
keyLen = MAX_MACRO_KEY_LEN-1;
strncpy(key, item, keyLen);
key[keyLen] = '\0';
return addItem(key, ++pos, charset);
}
//---------------------------------------------------------------
void CMacroTable::resetContent()
{
m_occupied = 0;
m_count = 0;
}
//---------------------------------------------------------------
const StdVnChar *CMacroTable::getKey(int idx) const
{
if (idx < 0 || idx >= m_count)
return 0;
return (StdVnChar *)(m_macroMem + m_table[idx].keyOffset);
}
//---------------------------------------------------------------
const StdVnChar *CMacroTable::getText(int idx) const
{
if (idx < 0 || idx >= m_count)
return 0;
return (StdVnChar *)(m_macroMem + m_table[idx].textOffset);
}