From 0a2e62f57734f820cd20e151d1150342408552ed Mon Sep 17 00:00:00 2001 From: gabrielgio Date: Sat, 17 Feb 2024 17:13:07 +0100 Subject: ref: Apply astyle --style=linux --- dict/main.c | 33 +- ext/spellfix.c | 4764 ++++++++++++++++++++++++++++--------------------------- importer/main.c | 31 +- lib/data.c | 24 +- lib/data.h | 8 +- lib/list.c | 14 +- lib/list.h | 3 +- lib/ui.c | 36 +- lib/ui.h | 8 +- lib/util.c | 17 +- lib/util.h | 2 - 11 files changed, 2518 insertions(+), 2422 deletions(-) diff --git a/dict/main.c b/dict/main.c index e5573fe..b743e07 100644 --- a/dict/main.c +++ b/dict/main.c @@ -14,24 +14,25 @@ Data *data; void search(char*, int); int run(const char*, const char*); -int main(int argc, char** argv) { +int main(int argc, char** argv) +{ int opt; char* txt = NULL; char* db = NULL; while ((opt = getopt(argc, argv, "t:d:h")) != -1) { switch(opt) { - case 't': - txt = copy_achar(optarg); - break; - case 'd': - db = copy_achar(optarg); - break; - case 'h': - // fall through - default: - printf("Usage: %s", argv[0]); - goto end; + case 't': + txt = copy_achar(optarg); + break; + case 'd': + db = copy_achar(optarg); + break; + case 'h': + // fall through + default: + printf("Usage: %s", argv[0]); + goto end; } } @@ -46,7 +47,8 @@ end: return r; } -int run(const char *db, const char *txt) { +int run(const char *db, const char *txt) +{ data = new_data(db); bootstrap(data); @@ -92,7 +94,8 @@ int run(const char *db, const char *txt) { return 0; } -void search(char *sch, int len) { +void search(char *sch, int len) +{ char s[len+2]; sprintf(s, "%%%*s%%", len, sch); @@ -107,5 +110,3 @@ void search(char *sch, int len) { } refresh(); } - - diff --git a/ext/spellfix.c b/ext/spellfix.c index a0c5aaf..77840a5 100644 --- a/ext/spellfix.c +++ b/ext/spellfix.c @@ -30,8 +30,8 @@ SQLITE_EXTENSION_INIT1 # include # define ALWAYS(X) 1 # define NEVER(X) 0 - typedef unsigned char u8; - typedef unsigned short u16; +typedef unsigned char u8; +typedef unsigned short u16; #endif #include @@ -73,99 +73,99 @@ SQLITE_EXTENSION_INIT1 ** characters. */ static const unsigned char midClass[] = { - /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, - /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, - /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, - /* */ CCLASS_SPACE, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, - /* */ CCLASS_SPACE, /* */ CCLASS_SPACE, /* */ CCLASS_OTHER, - /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, - /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, - /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, - /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, - /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, - /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_SPACE, - /* ! */ CCLASS_OTHER, /* " */ CCLASS_OTHER, /* # */ CCLASS_OTHER, - /* $ */ CCLASS_OTHER, /* % */ CCLASS_OTHER, /* & */ CCLASS_OTHER, - /* ' */ CCLASS_SILENT, /* ( */ CCLASS_OTHER, /* ) */ CCLASS_OTHER, - /* * */ CCLASS_OTHER, /* + */ CCLASS_OTHER, /* , */ CCLASS_OTHER, - /* - */ CCLASS_OTHER, /* . */ CCLASS_OTHER, /* / */ CCLASS_OTHER, - /* 0 */ CCLASS_DIGIT, /* 1 */ CCLASS_DIGIT, /* 2 */ CCLASS_DIGIT, - /* 3 */ CCLASS_DIGIT, /* 4 */ CCLASS_DIGIT, /* 5 */ CCLASS_DIGIT, - /* 6 */ CCLASS_DIGIT, /* 7 */ CCLASS_DIGIT, /* 8 */ CCLASS_DIGIT, - /* 9 */ CCLASS_DIGIT, /* : */ CCLASS_OTHER, /* ; */ CCLASS_OTHER, - /* < */ CCLASS_OTHER, /* = */ CCLASS_OTHER, /* > */ CCLASS_OTHER, - /* ? */ CCLASS_OTHER, /* @ */ CCLASS_OTHER, /* A */ CCLASS_VOWEL, - /* B */ CCLASS_B, /* C */ CCLASS_C, /* D */ CCLASS_D, - /* E */ CCLASS_VOWEL, /* F */ CCLASS_B, /* G */ CCLASS_C, - /* H */ CCLASS_SILENT, /* I */ CCLASS_VOWEL, /* J */ CCLASS_C, - /* K */ CCLASS_C, /* L */ CCLASS_L, /* M */ CCLASS_M, - /* N */ CCLASS_M, /* O */ CCLASS_VOWEL, /* P */ CCLASS_B, - /* Q */ CCLASS_C, /* R */ CCLASS_R, /* S */ CCLASS_C, - /* T */ CCLASS_D, /* U */ CCLASS_VOWEL, /* V */ CCLASS_B, - /* W */ CCLASS_B, /* X */ CCLASS_C, /* Y */ CCLASS_VOWEL, - /* Z */ CCLASS_C, /* [ */ CCLASS_OTHER, /* \ */ CCLASS_OTHER, - /* ] */ CCLASS_OTHER, /* ^ */ CCLASS_OTHER, /* _ */ CCLASS_OTHER, - /* ` */ CCLASS_OTHER, /* a */ CCLASS_VOWEL, /* b */ CCLASS_B, - /* c */ CCLASS_C, /* d */ CCLASS_D, /* e */ CCLASS_VOWEL, - /* f */ CCLASS_B, /* g */ CCLASS_C, /* h */ CCLASS_SILENT, - /* i */ CCLASS_VOWEL, /* j */ CCLASS_C, /* k */ CCLASS_C, - /* l */ CCLASS_L, /* m */ CCLASS_M, /* n */ CCLASS_M, - /* o */ CCLASS_VOWEL, /* p */ CCLASS_B, /* q */ CCLASS_C, - /* r */ CCLASS_R, /* s */ CCLASS_C, /* t */ CCLASS_D, - /* u */ CCLASS_VOWEL, /* v */ CCLASS_B, /* w */ CCLASS_B, - /* x */ CCLASS_C, /* y */ CCLASS_VOWEL, /* z */ CCLASS_C, - /* { */ CCLASS_OTHER, /* | */ CCLASS_OTHER, /* } */ CCLASS_OTHER, - /* ~ */ CCLASS_OTHER, /* */ CCLASS_OTHER, + /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, + /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, + /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, + /* */ CCLASS_SPACE, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, + /* */ CCLASS_SPACE, /* */ CCLASS_SPACE, /* */ CCLASS_OTHER, + /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, + /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, + /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, + /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, + /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, + /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_SPACE, + /* ! */ CCLASS_OTHER, /* " */ CCLASS_OTHER, /* # */ CCLASS_OTHER, + /* $ */ CCLASS_OTHER, /* % */ CCLASS_OTHER, /* & */ CCLASS_OTHER, + /* ' */ CCLASS_SILENT, /* ( */ CCLASS_OTHER, /* ) */ CCLASS_OTHER, + /* * */ CCLASS_OTHER, /* + */ CCLASS_OTHER, /* , */ CCLASS_OTHER, + /* - */ CCLASS_OTHER, /* . */ CCLASS_OTHER, /* / */ CCLASS_OTHER, + /* 0 */ CCLASS_DIGIT, /* 1 */ CCLASS_DIGIT, /* 2 */ CCLASS_DIGIT, + /* 3 */ CCLASS_DIGIT, /* 4 */ CCLASS_DIGIT, /* 5 */ CCLASS_DIGIT, + /* 6 */ CCLASS_DIGIT, /* 7 */ CCLASS_DIGIT, /* 8 */ CCLASS_DIGIT, + /* 9 */ CCLASS_DIGIT, /* : */ CCLASS_OTHER, /* ; */ CCLASS_OTHER, + /* < */ CCLASS_OTHER, /* = */ CCLASS_OTHER, /* > */ CCLASS_OTHER, + /* ? */ CCLASS_OTHER, /* @ */ CCLASS_OTHER, /* A */ CCLASS_VOWEL, + /* B */ CCLASS_B, /* C */ CCLASS_C, /* D */ CCLASS_D, + /* E */ CCLASS_VOWEL, /* F */ CCLASS_B, /* G */ CCLASS_C, + /* H */ CCLASS_SILENT, /* I */ CCLASS_VOWEL, /* J */ CCLASS_C, + /* K */ CCLASS_C, /* L */ CCLASS_L, /* M */ CCLASS_M, + /* N */ CCLASS_M, /* O */ CCLASS_VOWEL, /* P */ CCLASS_B, + /* Q */ CCLASS_C, /* R */ CCLASS_R, /* S */ CCLASS_C, + /* T */ CCLASS_D, /* U */ CCLASS_VOWEL, /* V */ CCLASS_B, + /* W */ CCLASS_B, /* X */ CCLASS_C, /* Y */ CCLASS_VOWEL, + /* Z */ CCLASS_C, /* [ */ CCLASS_OTHER, /* \ */ CCLASS_OTHER, + /* ] */ CCLASS_OTHER, /* ^ */ CCLASS_OTHER, /* _ */ CCLASS_OTHER, + /* ` */ CCLASS_OTHER, /* a */ CCLASS_VOWEL, /* b */ CCLASS_B, + /* c */ CCLASS_C, /* d */ CCLASS_D, /* e */ CCLASS_VOWEL, + /* f */ CCLASS_B, /* g */ CCLASS_C, /* h */ CCLASS_SILENT, + /* i */ CCLASS_VOWEL, /* j */ CCLASS_C, /* k */ CCLASS_C, + /* l */ CCLASS_L, /* m */ CCLASS_M, /* n */ CCLASS_M, + /* o */ CCLASS_VOWEL, /* p */ CCLASS_B, /* q */ CCLASS_C, + /* r */ CCLASS_R, /* s */ CCLASS_C, /* t */ CCLASS_D, + /* u */ CCLASS_VOWEL, /* v */ CCLASS_B, /* w */ CCLASS_B, + /* x */ CCLASS_C, /* y */ CCLASS_VOWEL, /* z */ CCLASS_C, + /* { */ CCLASS_OTHER, /* | */ CCLASS_OTHER, /* } */ CCLASS_OTHER, + /* ~ */ CCLASS_OTHER, /* */ CCLASS_OTHER, }; -/* +/* ** This tables gives the character class for ASCII characters that form the ** initial character of a word. The only difference from midClass is with ** the letters H, W, and Y. */ static const unsigned char initClass[] = { - /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, - /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, - /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, - /* */ CCLASS_SPACE, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, - /* */ CCLASS_SPACE, /* */ CCLASS_SPACE, /* */ CCLASS_OTHER, - /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, - /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, - /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, - /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, - /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, - /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_SPACE, - /* ! */ CCLASS_OTHER, /* " */ CCLASS_OTHER, /* # */ CCLASS_OTHER, - /* $ */ CCLASS_OTHER, /* % */ CCLASS_OTHER, /* & */ CCLASS_OTHER, - /* ' */ CCLASS_OTHER, /* ( */ CCLASS_OTHER, /* ) */ CCLASS_OTHER, - /* * */ CCLASS_OTHER, /* + */ CCLASS_OTHER, /* , */ CCLASS_OTHER, - /* - */ CCLASS_OTHER, /* . */ CCLASS_OTHER, /* / */ CCLASS_OTHER, - /* 0 */ CCLASS_DIGIT, /* 1 */ CCLASS_DIGIT, /* 2 */ CCLASS_DIGIT, - /* 3 */ CCLASS_DIGIT, /* 4 */ CCLASS_DIGIT, /* 5 */ CCLASS_DIGIT, - /* 6 */ CCLASS_DIGIT, /* 7 */ CCLASS_DIGIT, /* 8 */ CCLASS_DIGIT, - /* 9 */ CCLASS_DIGIT, /* : */ CCLASS_OTHER, /* ; */ CCLASS_OTHER, - /* < */ CCLASS_OTHER, /* = */ CCLASS_OTHER, /* > */ CCLASS_OTHER, - /* ? */ CCLASS_OTHER, /* @ */ CCLASS_OTHER, /* A */ CCLASS_VOWEL, - /* B */ CCLASS_B, /* C */ CCLASS_C, /* D */ CCLASS_D, - /* E */ CCLASS_VOWEL, /* F */ CCLASS_B, /* G */ CCLASS_C, - /* H */ CCLASS_SILENT, /* I */ CCLASS_VOWEL, /* J */ CCLASS_C, - /* K */ CCLASS_C, /* L */ CCLASS_L, /* M */ CCLASS_M, - /* N */ CCLASS_M, /* O */ CCLASS_VOWEL, /* P */ CCLASS_B, - /* Q */ CCLASS_C, /* R */ CCLASS_R, /* S */ CCLASS_C, - /* T */ CCLASS_D, /* U */ CCLASS_VOWEL, /* V */ CCLASS_B, - /* W */ CCLASS_B, /* X */ CCLASS_C, /* Y */ CCLASS_Y, - /* Z */ CCLASS_C, /* [ */ CCLASS_OTHER, /* \ */ CCLASS_OTHER, - /* ] */ CCLASS_OTHER, /* ^ */ CCLASS_OTHER, /* _ */ CCLASS_OTHER, - /* ` */ CCLASS_OTHER, /* a */ CCLASS_VOWEL, /* b */ CCLASS_B, - /* c */ CCLASS_C, /* d */ CCLASS_D, /* e */ CCLASS_VOWEL, - /* f */ CCLASS_B, /* g */ CCLASS_C, /* h */ CCLASS_SILENT, - /* i */ CCLASS_VOWEL, /* j */ CCLASS_C, /* k */ CCLASS_C, - /* l */ CCLASS_L, /* m */ CCLASS_M, /* n */ CCLASS_M, - /* o */ CCLASS_VOWEL, /* p */ CCLASS_B, /* q */ CCLASS_C, - /* r */ CCLASS_R, /* s */ CCLASS_C, /* t */ CCLASS_D, - /* u */ CCLASS_VOWEL, /* v */ CCLASS_B, /* w */ CCLASS_B, - /* x */ CCLASS_C, /* y */ CCLASS_Y, /* z */ CCLASS_C, - /* { */ CCLASS_OTHER, /* | */ CCLASS_OTHER, /* } */ CCLASS_OTHER, - /* ~ */ CCLASS_OTHER, /* */ CCLASS_OTHER, + /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, + /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, + /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, + /* */ CCLASS_SPACE, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, + /* */ CCLASS_SPACE, /* */ CCLASS_SPACE, /* */ CCLASS_OTHER, + /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, + /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, + /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, + /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, + /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, + /* */ CCLASS_OTHER, /* */ CCLASS_OTHER, /* */ CCLASS_SPACE, + /* ! */ CCLASS_OTHER, /* " */ CCLASS_OTHER, /* # */ CCLASS_OTHER, + /* $ */ CCLASS_OTHER, /* % */ CCLASS_OTHER, /* & */ CCLASS_OTHER, + /* ' */ CCLASS_OTHER, /* ( */ CCLASS_OTHER, /* ) */ CCLASS_OTHER, + /* * */ CCLASS_OTHER, /* + */ CCLASS_OTHER, /* , */ CCLASS_OTHER, + /* - */ CCLASS_OTHER, /* . */ CCLASS_OTHER, /* / */ CCLASS_OTHER, + /* 0 */ CCLASS_DIGIT, /* 1 */ CCLASS_DIGIT, /* 2 */ CCLASS_DIGIT, + /* 3 */ CCLASS_DIGIT, /* 4 */ CCLASS_DIGIT, /* 5 */ CCLASS_DIGIT, + /* 6 */ CCLASS_DIGIT, /* 7 */ CCLASS_DIGIT, /* 8 */ CCLASS_DIGIT, + /* 9 */ CCLASS_DIGIT, /* : */ CCLASS_OTHER, /* ; */ CCLASS_OTHER, + /* < */ CCLASS_OTHER, /* = */ CCLASS_OTHER, /* > */ CCLASS_OTHER, + /* ? */ CCLASS_OTHER, /* @ */ CCLASS_OTHER, /* A */ CCLASS_VOWEL, + /* B */ CCLASS_B, /* C */ CCLASS_C, /* D */ CCLASS_D, + /* E */ CCLASS_VOWEL, /* F */ CCLASS_B, /* G */ CCLASS_C, + /* H */ CCLASS_SILENT, /* I */ CCLASS_VOWEL, /* J */ CCLASS_C, + /* K */ CCLASS_C, /* L */ CCLASS_L, /* M */ CCLASS_M, + /* N */ CCLASS_M, /* O */ CCLASS_VOWEL, /* P */ CCLASS_B, + /* Q */ CCLASS_C, /* R */ CCLASS_R, /* S */ CCLASS_C, + /* T */ CCLASS_D, /* U */ CCLASS_VOWEL, /* V */ CCLASS_B, + /* W */ CCLASS_B, /* X */ CCLASS_C, /* Y */ CCLASS_Y, + /* Z */ CCLASS_C, /* [ */ CCLASS_OTHER, /* \ */ CCLASS_OTHER, + /* ] */ CCLASS_OTHER, /* ^ */ CCLASS_OTHER, /* _ */ CCLASS_OTHER, + /* ` */ CCLASS_OTHER, /* a */ CCLASS_VOWEL, /* b */ CCLASS_B, + /* c */ CCLASS_C, /* d */ CCLASS_D, /* e */ CCLASS_VOWEL, + /* f */ CCLASS_B, /* g */ CCLASS_C, /* h */ CCLASS_SILENT, + /* i */ CCLASS_VOWEL, /* j */ CCLASS_C, /* k */ CCLASS_C, + /* l */ CCLASS_L, /* m */ CCLASS_M, /* n */ CCLASS_M, + /* o */ CCLASS_VOWEL, /* p */ CCLASS_B, /* q */ CCLASS_C, + /* r */ CCLASS_R, /* s */ CCLASS_C, /* t */ CCLASS_D, + /* u */ CCLASS_VOWEL, /* v */ CCLASS_B, /* w */ CCLASS_B, + /* x */ CCLASS_C, /* y */ CCLASS_Y, /* z */ CCLASS_C, + /* { */ CCLASS_OTHER, /* | */ CCLASS_OTHER, /* } */ CCLASS_OTHER, + /* ~ */ CCLASS_OTHER, /* */ CCLASS_OTHER, }; /* @@ -189,54 +189,58 @@ static const unsigned char className[] = ".ABCDHLRMY9 ?"; ** ** Space to hold the result is obtained from sqlite3_malloc() ** -** Return NULL if memory allocation fails. -*/ -static unsigned char *phoneticHash(const unsigned char *zIn, int nIn){ - unsigned char *zOut = sqlite3_malloc64( nIn + 1 ); - int i; - int nOut = 0; - char cPrev = 0x77; - char cPrevX = 0x77; - const unsigned char *aClass = initClass; - - if( zOut==0 ) return 0; - if( nIn>2 ){ - switch( zIn[0] ){ - case 'g': - case 'k': { - if( zIn[1]=='n' ){ zIn++; nIn--; } - break; - } - } - } - for(i=0; i=0 ); - if( nOut==0 || c!=zOut[nOut-1] ) zOut[nOut++] = c; - } - zOut[nOut] = 0; - return zOut; +** Return NULL if memory allocation fails. +*/ +static unsigned char *phoneticHash(const unsigned char *zIn, int nIn) +{ + unsigned char *zOut = sqlite3_malloc64( nIn + 1 ); + int i; + int nOut = 0; + char cPrev = 0x77; + char cPrevX = 0x77; + const unsigned char *aClass = initClass; + + if( zOut==0 ) return 0; + if( nIn>2 ) { + switch( zIn[0] ) { + case 'g': + case 'k': { + if( zIn[1]=='n' ) { + zIn++; + nIn--; + } + break; + } + } + } + for(i=0; i=0 ); + if( nOut==0 || c!=zOut[nOut-1] ) zOut[nOut++] = c; + } + zOut[nOut] = 0; + return zOut; } /* @@ -244,29 +248,31 @@ static unsigned char *phoneticHash(const unsigned char *zIn, int nIn){ ** the description of phoneticHash() for additional information. */ static void phoneticHashSqlFunc( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - const unsigned char *zIn; - unsigned char *zOut; - - zIn = sqlite3_value_text(argv[0]); - if( zIn==0 ) return; - zOut = phoneticHash(zIn, sqlite3_value_bytes(argv[0])); - if( zOut==0 ){ - sqlite3_result_error_nomem(context); - }else{ - sqlite3_result_text(context, (char*)zOut, -1, sqlite3_free); - } + sqlite3_context *context, + int argc, + sqlite3_value **argv +) +{ + const unsigned char *zIn; + unsigned char *zOut; + + zIn = sqlite3_value_text(argv[0]); + if( zIn==0 ) return; + zOut = phoneticHash(zIn, sqlite3_value_bytes(argv[0])); + if( zOut==0 ) { + sqlite3_result_error_nomem(context); + } else { + sqlite3_result_text(context, (char*)zOut, -1, sqlite3_free); + } } /* ** Return the character class number for a character given its ** context. */ -static char characterClass(char cPrev, char c){ - return cPrev==0 ? initClass[c&0x7f] : midClass[c&0x7f]; +static char characterClass(char cPrev, char c) +{ + return cPrev==0 ? initClass[c&0x7f] : midClass[c&0x7f]; } /* @@ -274,34 +280,35 @@ static char characterClass(char cPrev, char c){ ** following character cPrev. If cPrev==0, that means c is the first ** character of the word. */ -static int insertOrDeleteCost(char cPrev, char c, char cNext){ - char classC = characterClass(cPrev, c); - char classCprev; +static int insertOrDeleteCost(char cPrev, char c, char cNext) +{ + char classC = characterClass(cPrev, c); + char classCprev; + + if( classC==CCLASS_SILENT ) { + /* Insert or delete "silent" characters such as H or W */ + return 1; + } + if( cPrev==c ) { + /* Repeated characters, or miss a repeat */ + return 10; + } + if( classC==CCLASS_VOWEL && (cPrev=='r' || cNext=='r') ) { + return 20; /* Insert a vowel before or after 'r' */ + } + classCprev = characterClass(cPrev, cPrev); + if( classC==classCprev ) { + if( classC==CCLASS_VOWEL ) { + /* Remove or add a new vowel to a vowel cluster */ + return 15; + } else { + /* Remove or add a consonant not in the same class */ + return 50; + } + } - if( classC==CCLASS_SILENT ){ - /* Insert or delete "silent" characters such as H or W */ - return 1; - } - if( cPrev==c ){ - /* Repeated characters, or miss a repeat */ - return 10; - } - if( classC==CCLASS_VOWEL && (cPrev=='r' || cNext=='r') ){ - return 20; /* Insert a vowel before or after 'r' */ - } - classCprev = characterClass(cPrev, cPrev); - if( classC==classCprev ){ - if( classC==CCLASS_VOWEL ){ - /* Remove or add a new vowel to a vowel cluster */ - return 15; - }else{ - /* Remove or add a consonant not in the same class */ - return 50; - } - } - - /* any other character insertion or deletion */ - return 100; + /* any other character insertion or deletion */ + return 100; } /* @@ -315,29 +322,30 @@ static int insertOrDeleteCost(char cPrev, char c, char cNext){ ** the previous character is cPrev. If cPrev==0 then cTo is the first ** character of the word. */ -static int substituteCost(char cPrev, char cFrom, char cTo){ - char classFrom, classTo; - if( cFrom==cTo ){ - /* Exact match */ - return 0; - } - if( cFrom==(cTo^0x20) && ((cTo>='A' && cTo<='Z') || (cTo>='a' && cTo<='z')) ){ - /* differ only in case */ - return 0; - } - classFrom = characterClass(cPrev, cFrom); - classTo = characterClass(cPrev, cTo); - if( classFrom==classTo ){ - /* Same character class */ - return 40; - } - if( classFrom>=CCLASS_B && classFrom<=CCLASS_Y - && classTo>=CCLASS_B && classTo<=CCLASS_Y ){ - /* Convert from one consonant to another, but in a different class */ - return 75; - } - /* Any other subsitution */ - return 100; +static int substituteCost(char cPrev, char cFrom, char cTo) +{ + char classFrom, classTo; + if( cFrom==cTo ) { + /* Exact match */ + return 0; + } + if( cFrom==(cTo^0x20) && ((cTo>='A' && cTo<='Z') || (cTo>='a' && cTo<='z')) ) { + /* differ only in case */ + return 0; + } + classFrom = characterClass(cPrev, cFrom); + classTo = characterClass(cPrev, cTo); + if( classFrom==classTo ) { + /* Same character class */ + return 40; + } + if( classFrom>=CCLASS_B && classFrom<=CCLASS_Y + && classTo>=CCLASS_B && classTo<=CCLASS_Y ) { + /* Convert from one consonant to another, but in a different class */ + return 75; + } + /* Any other subsitution */ + return 100; } /* @@ -351,7 +359,7 @@ static int substituteCost(char cPrev, char cFrom, char cTo){ ** Negative values indicate an error: ** -1 One of the inputs is NULL ** -2 Non-ASCII characters on input -** -3 Unable to allocate memory +** -3 Unable to allocate memory ** ** If pnMatch is not NULL, then *pnMatch is set to the number of bytes ** of zB that matched the pattern in zA. If zA does not end with a '*', @@ -359,152 +367,158 @@ static int substituteCost(char cPrev, char cFrom, char cTo){ ** If zA does end in a '*', then it is the number of bytes in the prefix ** of zB that was deemed to match zA. */ -static int editdist1(const char *zA, const char *zB, int *pnMatch){ - int nA, nB; /* Number of characters in zA[] and zB[] */ - int xA, xB; /* Loop counters for zA[] and zB[] */ - char cA = 0, cB; /* Current character of zA and zB */ - char cAprev, cBprev; /* Previous character of zA and zB */ - char cAnext, cBnext; /* Next character in zA and zB */ - int d; /* North-west cost value */ - int dc = 0; /* North-west character value */ - int res; /* Final result */ - int *m; /* The cost matrix */ - char *cx; /* Corresponding character values */ - int *toFree = 0; /* Malloced space */ - int nMatch = 0; - int mStack[60+15]; /* Stack space to use if not too much is needed */ - - /* Early out if either input is NULL */ - if( zA==0 || zB==0 ) return -1; - - /* Skip any common prefix */ - while( zA[0] && zA[0]==zB[0] ){ dc = zA[0]; zA++; zB++; nMatch++; } - if( pnMatch ) *pnMatch = nMatch; - if( zA[0]==0 && zB[0]==0 ) return 0; +static int editdist1(const char *zA, const char *zB, int *pnMatch) +{ + int nA, nB; /* Number of characters in zA[] and zB[] */ + int xA, xB; /* Loop counters for zA[] and zB[] */ + char cA = 0, cB; /* Current character of zA and zB */ + char cAprev, cBprev; /* Previous character of zA and zB */ + char cAnext, cBnext; /* Next character in zA and zB */ + int d; /* North-west cost value */ + int dc = 0; /* North-west character value */ + int res; /* Final result */ + int *m; /* The cost matrix */ + char *cx; /* Corresponding character values */ + int *toFree = 0; /* Malloced space */ + int nMatch = 0; + int mStack[60+15]; /* Stack space to use if not too much is needed */ + + /* Early out if either input is NULL */ + if( zA==0 || zB==0 ) return -1; + + /* Skip any common prefix */ + while( zA[0] && zA[0]==zB[0] ) { + dc = zA[0]; + zA++; + zB++; + nMatch++; + } + if( pnMatch ) *pnMatch = nMatch; + if( zA[0]==0 && zB[0]==0 ) return 0; #if 0 - printf("A=\"%s\" B=\"%s\" dc=%c\n", zA, zB, dc?dc:' '); + printf("A=\"%s\" B=\"%s\" dc=%c\n", zA, zB, dc?dc:' '); #endif - /* Verify input strings and measure their lengths */ - for(nA=0; zA[nA]; nA++){ - if( zA[nA]&0x80 ) return -2; - } - for(nB=0; zB[nB]; nB++){ - if( zB[nB]&0x80 ) return -2; - } + /* Verify input strings and measure their lengths */ + for(nA=0; zA[nA]; nA++) { + if( zA[nA]&0x80 ) return -2; + } + for(nB=0; zB[nB]; nB++) { + if( zB[nB]&0x80 ) return -2; + } + + /* Special processing if either string is empty */ + if( nA==0 ) { + cBprev = (char)dc; + for(xB=res=0; (cB = zB[xB])!=0; xB++) { + res += insertOrDeleteCost(cBprev, cB, zB[xB+1])/FINAL_INS_COST_DIV; + cBprev = cB; + } + return res; + } + if( nB==0 ) { + cAprev = (char)dc; + for(xA=res=0; (cA = zA[xA])!=0; xA++) { + res += insertOrDeleteCost(cAprev, cA, zA[xA+1]); + cAprev = cA; + } + return res; + } + + /* A is a prefix of B */ + if( zA[0]=='*' && zA[1]==0 ) return 0; + + /* Allocate and initialize the Wagner matrix */ + if( nB<(sizeof(mStack)*4)/(sizeof(mStack[0])*5) ) { + m = mStack; + } else { + m = toFree = sqlite3_malloc64( (nB+1)*5*sizeof(m[0])/4 ); + if( m==0 ) return -3; + } + cx = (char*)&m[nB+1]; - /* Special processing if either string is empty */ - if( nA==0 ){ + /* Compute the Wagner edit distance */ + m[0] = 0; + cx[0] = (char)dc; cBprev = (char)dc; - for(xB=res=0; (cB = zB[xB])!=0; xB++){ - res += insertOrDeleteCost(cBprev, cB, zB[xB+1])/FINAL_INS_COST_DIV; - cBprev = cB; + for(xB=1; xB<=nB; xB++) { + cBnext = zB[xB]; + cB = zB[xB-1]; + cx[xB] = cB; + m[xB] = m[xB-1] + insertOrDeleteCost(cBprev, cB, cBnext); + cBprev = cB; } - return res; - } - if( nB==0 ){ cAprev = (char)dc; - for(xA=res=0; (cA = zA[xA])!=0; xA++){ - res += insertOrDeleteCost(cAprev, cA, zA[xA+1]); - cAprev = cA; - } - return res; - } - - /* A is a prefix of B */ - if( zA[0]=='*' && zA[1]==0 ) return 0; - - /* Allocate and initialize the Wagner matrix */ - if( nB<(sizeof(mStack)*4)/(sizeof(mStack[0])*5) ){ - m = mStack; - }else{ - m = toFree = sqlite3_malloc64( (nB+1)*5*sizeof(m[0])/4 ); - if( m==0 ) return -3; - } - cx = (char*)&m[nB+1]; - - /* Compute the Wagner edit distance */ - m[0] = 0; - cx[0] = (char)dc; - cBprev = (char)dc; - for(xB=1; xB<=nB; xB++){ - cBnext = zB[xB]; - cB = zB[xB-1]; - cx[xB] = cB; - m[xB] = m[xB-1] + insertOrDeleteCost(cBprev, cB, cBnext); - cBprev = cB; - } - cAprev = (char)dc; - for(xA=1; xA<=nA; xA++){ - int lastA = (xA==nA); - cA = zA[xA-1]; - cAnext = zA[xA]; - if( cA=='*' && lastA ) break; - d = m[0]; - dc = cx[0]; - m[0] = d + insertOrDeleteCost(cAprev, cA, cAnext); - cBprev = 0; - for(xB=1; xB<=nB; xB++){ - int totalCost, insCost, delCost, subCost, ncx; - cB = zB[xB-1]; - cBnext = zB[xB]; - - /* Cost to insert cB */ - insCost = insertOrDeleteCost(cx[xB-1], cB, cBnext); - if( lastA ) insCost /= FINAL_INS_COST_DIV; - - /* Cost to delete cA */ - delCost = insertOrDeleteCost(cx[xB], cA, cBnext); - - /* Cost to substitute cA->cB */ - subCost = substituteCost(cx[xB-1], cA, cB); - - /* Best cost */ - totalCost = insCost + m[xB-1]; - ncx = cB; - if( (delCost + m[xB])cB */ + subCost = substituteCost(cx[xB-1], cA, cB); + + /* Best cost */ + totalCost = insCost + m[xB-1]; + ncx = cB; + if( (delCost + m[xB])nLang; i++){ - EditDist3Cost *pCost, *pNext; - pCost = p->a[i].pCost; - while( pCost ){ - pNext = pCost->pNext; - sqlite3_free(pCost); - pCost = pNext; - } - } - sqlite3_free(p->a); - memset(p, 0, sizeof(*p)); +static void editDist3ConfigClear(EditDist3Config *p) +{ + int i; + if( p==0 ) return; + for(i=0; inLang; i++) { + EditDist3Cost *pCost, *pNext; + pCost = p->a[i].pCost; + while( pCost ) { + pNext = pCost->pNext; + sqlite3_free(pCost); + pCost = pNext; + } + } + sqlite3_free(p->a); + memset(p, 0, sizeof(*p)); } -static void editDist3ConfigDelete(void *pIn){ - EditDist3Config *p = (EditDist3Config*)pIn; - editDist3ConfigClear(p); - sqlite3_free(p); +static void editDist3ConfigDelete(void *pIn) +{ + EditDist3Config *p = (EditDist3Config*)pIn; + editDist3ConfigClear(p); + sqlite3_free(p); } /* Compare the FROM values of two EditDist3Cost objects, for sorting. ** Return negative, zero, or positive if the A is less than, equal to, ** or greater than B. */ -static int editDist3CostCompare(EditDist3Cost *pA, EditDist3Cost *pB){ - int n = pA->nFrom; - int rc; - if( n>pB->nFrom ) n = pB->nFrom; - rc = strncmp(pA->a, pB->a, n); - if( rc==0 ) rc = pA->nFrom - pB->nFrom; - return rc; +static int editDist3CostCompare(EditDist3Cost *pA, EditDist3Cost *pB) +{ + int n = pA->nFrom; + int rc; + if( n>pB->nFrom ) n = pB->nFrom; + rc = strncmp(pA->a, pB->a, n); + if( rc==0 ) rc = pA->nFrom - pB->nFrom; + return rc; } /* @@ -676,183 +692,195 @@ static int editDist3CostCompare(EditDist3Cost *pA, EditDist3Cost *pB){ ** of increasing FROM. */ static EditDist3Cost *editDist3CostMerge( - EditDist3Cost *pA, - EditDist3Cost *pB -){ - EditDist3Cost *pHead = 0; - EditDist3Cost **ppTail = &pHead; - EditDist3Cost *p; - while( pA && pB ){ - if( editDist3CostCompare(pA,pB)<=0 ){ - p = pA; - pA = pA->pNext; - }else{ - p = pB; - pB = pB->pNext; - } - *ppTail = p; - ppTail = &p->pNext; - } - if( pA ){ - *ppTail = pA; - }else{ - *ppTail = pB; - } - return pHead; + EditDist3Cost *pA, + EditDist3Cost *pB +) +{ + EditDist3Cost *pHead = 0; + EditDist3Cost **ppTail = &pHead; + EditDist3Cost *p; + while( pA && pB ) { + if( editDist3CostCompare(pA,pB)<=0 ) { + p = pA; + pA = pA->pNext; + } else { + p = pB; + pB = pB->pNext; + } + *ppTail = p; + ppTail = &p->pNext; + } + if( pA ) { + *ppTail = pA; + } else { + *ppTail = pB; + } + return pHead; } /* ** Sort a list of EditDist3Cost objects into order of increasing FROM */ -static EditDist3Cost *editDist3CostSort(EditDist3Cost *pList){ - EditDist3Cost *ap[60], *p; - int i; - int mx = 0; - ap[0] = 0; - ap[1] = 0; - while( pList ){ - p = pList; - pList = p->pNext; - p->pNext = 0; - for(i=0; ap[i]; i++){ - p = editDist3CostMerge(ap[i],p); - ap[i] = 0; - } - ap[i] = p; - if( i>mx ){ - mx = i; - ap[i+1] = 0; - } - } - p = 0; - for(i=0; i<=mx; i++){ - if( ap[i] ) p = editDist3CostMerge(p,ap[i]); - } - return p; +static EditDist3Cost *editDist3CostSort(EditDist3Cost *pList) +{ + EditDist3Cost *ap[60], *p; + int i; + int mx = 0; + ap[0] = 0; + ap[1] = 0; + while( pList ) { + p = pList; + pList = p->pNext; + p->pNext = 0; + for(i=0; ap[i]; i++) { + p = editDist3CostMerge(ap[i],p); + ap[i] = 0; + } + ap[i] = p; + if( i>mx ) { + mx = i; + ap[i+1] = 0; + } + } + p = 0; + for(i=0; i<=mx; i++) { + if( ap[i] ) p = editDist3CostMerge(p,ap[i]); + } + return p; } /* ** Load all edit-distance weights from a table. */ static int editDist3ConfigLoad( - EditDist3Config *p, /* The edit distance configuration to load */ - sqlite3 *db, /* Load from this database */ - const char *zTable /* Name of the table from which to load */ -){ - sqlite3_stmt *pStmt; - int rc, rc2; - char *zSql; - int iLangPrev = -9999; - EditDist3Lang *pLang = 0; - - zSql = sqlite3_mprintf("SELECT iLang, cFrom, cTo, iCost" - " FROM \"%w\" WHERE iLang>=0 ORDER BY iLang", zTable); - if( zSql==0 ) return SQLITE_NOMEM; - rc = sqlite3_prepare(db, zSql, -1, &pStmt, 0); - sqlite3_free(zSql); - if( rc ) return rc; - editDist3ConfigClear(p); - while( sqlite3_step(pStmt)==SQLITE_ROW ){ - int iLang = sqlite3_column_int(pStmt, 0); - const char *zFrom = (const char*)sqlite3_column_text(pStmt, 1); - int nFrom = zFrom ? sqlite3_column_bytes(pStmt, 1) : 0; - const char *zTo = (const char*)sqlite3_column_text(pStmt, 2); - int nTo = zTo ? sqlite3_column_bytes(pStmt, 2) : 0; - int iCost = sqlite3_column_int(pStmt, 3); - - assert( zFrom!=0 || nFrom==0 ); - assert( zTo!=0 || nTo==0 ); - if( nFrom>100 || nTo>100 ) continue; - if( iCost<0 ) continue; - if( iCost>=10000 ) continue; /* Costs above 10K are considered infinite */ - if( pLang==0 || iLang!=iLangPrev ){ - EditDist3Lang *pNew; - pNew = sqlite3_realloc64(p->a, (p->nLang+1)*sizeof(p->a[0])); - if( pNew==0 ){ rc = SQLITE_NOMEM; break; } - p->a = pNew; - pLang = &p->a[p->nLang]; - p->nLang++; - pLang->iLang = iLang; - pLang->iInsCost = 100; - pLang->iDelCost = 100; - pLang->iSubCost = 150; - pLang->pCost = 0; - iLangPrev = iLang; - } - if( nFrom==1 && zFrom[0]=='?' && nTo==0 ){ - pLang->iDelCost = iCost; - }else if( nFrom==0 && nTo==1 && zTo[0]=='?' ){ - pLang->iInsCost = iCost; - }else if( nFrom==1 && nTo==1 && zFrom[0]=='?' && zTo[0]=='?' ){ - pLang->iSubCost = iCost; - }else{ - EditDist3Cost *pCost; - int nExtra = nFrom + nTo - 4; - if( nExtra<0 ) nExtra = 0; - pCost = sqlite3_malloc64( sizeof(*pCost) + nExtra ); - if( pCost==0 ){ rc = SQLITE_NOMEM; break; } - pCost->nFrom = (u8)nFrom; - pCost->nTo = (u8)nTo; - pCost->iCost = (u16)iCost; - memcpy(pCost->a, zFrom, nFrom); - memcpy(pCost->a + nFrom, zTo, nTo); - pCost->pNext = pLang->pCost; - pLang->pCost = pCost; - } - } - rc2 = sqlite3_finalize(pStmt); - if( rc==SQLITE_OK ) rc = rc2; - if( rc==SQLITE_OK ){ - int iLang; - for(iLang=0; iLangnLang; iLang++){ - p->a[iLang].pCost = editDist3CostSort(p->a[iLang].pCost); - } - } - return rc; + EditDist3Config *p, /* The edit distance configuration to load */ + sqlite3 *db, /* Load from this database */ + const char *zTable /* Name of the table from which to load */ +) +{ + sqlite3_stmt *pStmt; + int rc, rc2; + char *zSql; + int iLangPrev = -9999; + EditDist3Lang *pLang = 0; + + zSql = sqlite3_mprintf("SELECT iLang, cFrom, cTo, iCost" + " FROM \"%w\" WHERE iLang>=0 ORDER BY iLang", zTable); + if( zSql==0 ) return SQLITE_NOMEM; + rc = sqlite3_prepare(db, zSql, -1, &pStmt, 0); + sqlite3_free(zSql); + if( rc ) return rc; + editDist3ConfigClear(p); + while( sqlite3_step(pStmt)==SQLITE_ROW ) { + int iLang = sqlite3_column_int(pStmt, 0); + const char *zFrom = (const char*)sqlite3_column_text(pStmt, 1); + int nFrom = zFrom ? sqlite3_column_bytes(pStmt, 1) : 0; + const char *zTo = (const char*)sqlite3_column_text(pStmt, 2); + int nTo = zTo ? sqlite3_column_bytes(pStmt, 2) : 0; + int iCost = sqlite3_column_int(pStmt, 3); + + assert( zFrom!=0 || nFrom==0 ); + assert( zTo!=0 || nTo==0 ); + if( nFrom>100 || nTo>100 ) continue; + if( iCost<0 ) continue; + if( iCost>=10000 ) continue; /* Costs above 10K are considered infinite */ + if( pLang==0 || iLang!=iLangPrev ) { + EditDist3Lang *pNew; + pNew = sqlite3_realloc64(p->a, (p->nLang+1)*sizeof(p->a[0])); + if( pNew==0 ) { + rc = SQLITE_NOMEM; + break; + } + p->a = pNew; + pLang = &p->a[p->nLang]; + p->nLang++; + pLang->iLang = iLang; + pLang->iInsCost = 100; + pLang->iDelCost = 100; + pLang->iSubCost = 150; + pLang->pCost = 0; + iLangPrev = iLang; + } + if( nFrom==1 && zFrom[0]=='?' && nTo==0 ) { + pLang->iDelCost = iCost; + } else if( nFrom==0 && nTo==1 && zTo[0]=='?' ) { + pLang->iInsCost = iCost; + } else if( nFrom==1 && nTo==1 && zFrom[0]=='?' && zTo[0]=='?' ) { + pLang->iSubCost = iCost; + } else { + EditDist3Cost *pCost; + int nExtra = nFrom + nTo - 4; + if( nExtra<0 ) nExtra = 0; + pCost = sqlite3_malloc64( sizeof(*pCost) + nExtra ); + if( pCost==0 ) { + rc = SQLITE_NOMEM; + break; + } + pCost->nFrom = (u8)nFrom; + pCost->nTo = (u8)nTo; + pCost->iCost = (u16)iCost; + memcpy(pCost->a, zFrom, nFrom); + memcpy(pCost->a + nFrom, zTo, nTo); + pCost->pNext = pLang->pCost; + pLang->pCost = pCost; + } + } + rc2 = sqlite3_finalize(pStmt); + if( rc==SQLITE_OK ) rc = rc2; + if( rc==SQLITE_OK ) { + int iLang; + for(iLang=0; iLangnLang; iLang++) { + p->a[iLang].pCost = editDist3CostSort(p->a[iLang].pCost); + } + } + return rc; } /* ** Return the length (in bytes) of a utf-8 character. Or return a maximum ** of N. */ -static int utf8Len(unsigned char c, int N){ - int len = 1; - if( c>0x7f ){ - if( (c&0xe0)==0xc0 ){ - len = 2; - }else if( (c&0xf0)==0xe0 ){ - len = 3; - }else{ - len = 4; - } - } - if( len>N ) len = N; - return len; +static int utf8Len(unsigned char c, int N) +{ + int len = 1; + if( c>0x7f ) { + if( (c&0xe0)==0xc0 ) { + len = 2; + } else if( (c&0xf0)==0xe0 ) { + len = 3; + } else { + len = 4; + } + } + if( len>N ) len = N; + return len; } /* ** Return TRUE (non-zero) if the To side of the given cost matches ** the given string. */ -static int matchTo(EditDist3Cost *p, const char *z, int n){ - assert( n>0 ); - if( p->a[p->nFrom]!=z[0] ) return 0; - if( p->nTo>n ) return 0; - if( strncmp(p->a+p->nFrom, z, p->nTo)!=0 ) return 0; - return 1; +static int matchTo(EditDist3Cost *p, const char *z, int n) +{ + assert( n>0 ); + if( p->a[p->nFrom]!=z[0] ) return 0; + if( p->nTo>n ) return 0; + if( strncmp(p->a+p->nFrom, z, p->nTo)!=0 ) return 0; + return 1; } /* ** Return TRUE (non-zero) if the From side of the given cost matches ** the given string. */ -static int matchFrom(EditDist3Cost *p, const char *z, int n){ - assert( p->nFrom<=n ); - if( p->nFrom ){ - if( p->a[0]!=z[0] ) return 0; - if( strncmp(p->a, z, p->nFrom)!=0 ) return 0; - } - return 1; +static int matchFrom(EditDist3Cost *p, const char *z, int n) +{ + assert( p->nFrom<=n ); + if( p->nFrom ) { + if( p->a[0]!=z[0] ) return 0; + if( strncmp(p->a, z, p->nFrom)!=0 ) return 0; + } + return 1; } /* @@ -860,92 +888,95 @@ static int matchFrom(EditDist3Cost *p, const char *z, int n){ ** character are the same. */ static int matchFromTo( - EditDist3FromString *pStr, /* Left hand string */ - int n1, /* Index of comparison character on the left */ - const char *z2, /* Right-handl comparison character */ - int n2 /* Bytes remaining in z2[] */ -){ - int b1 = pStr->a[n1].nByte; - if( b1>n2 ) return 0; - assert( b1>0 ); - if( pStr->z[n1]!=z2[0] ) return 0; - if( strncmp(pStr->z+n1, z2, b1)!=0 ) return 0; - return 1; + EditDist3FromString *pStr, /* Left hand string */ + int n1, /* Index of comparison character on the left */ + const char *z2, /* Right-handl comparison character */ + int n2 /* Bytes remaining in z2[] */ +) +{ + int b1 = pStr->a[n1].nByte; + if( b1>n2 ) return 0; + assert( b1>0 ); + if( pStr->z[n1]!=z2[0] ) return 0; + if( strncmp(pStr->z+n1, z2, b1)!=0 ) return 0; + return 1; } /* ** Delete an EditDist3FromString objecct */ -static void editDist3FromStringDelete(EditDist3FromString *p){ - int i; - if( p ){ - for(i=0; in; i++){ - sqlite3_free(p->a[i].apDel); - sqlite3_free(p->a[i].apSubst); +static void editDist3FromStringDelete(EditDist3FromString *p) +{ + int i; + if( p ) { + for(i=0; in; i++) { + sqlite3_free(p->a[i].apDel); + sqlite3_free(p->a[i].apSubst); + } + sqlite3_free(p); } - sqlite3_free(p); - } } /* ** Create a EditDist3FromString object. */ static EditDist3FromString *editDist3FromStringNew( - const EditDist3Lang *pLang, - const char *z, - int n -){ - EditDist3FromString *pStr; - EditDist3Cost *p; - int i; - - if( z==0 ) return 0; - if( n<0 ) n = (int)strlen(z); - pStr = sqlite3_malloc64( sizeof(*pStr) + sizeof(pStr->a[0])*n + n + 1 ); - if( pStr==0 ) return 0; - pStr->a = (EditDist3From*)&pStr[1]; - memset(pStr->a, 0, sizeof(pStr->a[0])*n); - pStr->n = n; - pStr->z = (char*)&pStr->a[n]; - memcpy(pStr->z, z, n+1); - if( n && z[n-1]=='*' ){ - pStr->isPrefix = 1; - n--; - pStr->n--; - pStr->z[n] = 0; - }else{ - pStr->isPrefix = 0; - } - - for(i=0; ia[i]; - memset(pFrom, 0, sizeof(*pFrom)); - pFrom->nByte = utf8Len((unsigned char)z[i], n-i); - for(p=pLang->pCost; p; p=p->pNext){ - EditDist3Cost **apNew; - if( i+p->nFrom>n ) continue; - if( matchFrom(p, z+i, n-i)==0 ) continue; - if( p->nTo==0 ){ - apNew = sqlite3_realloc64(pFrom->apDel, - sizeof(*apNew)*(pFrom->nDel+1)); - if( apNew==0 ) break; - pFrom->apDel = apNew; - apNew[pFrom->nDel++] = p; - }else{ - apNew = sqlite3_realloc64(pFrom->apSubst, - sizeof(*apNew)*(pFrom->nSubst+1)); - if( apNew==0 ) break; - pFrom->apSubst = apNew; - apNew[pFrom->nSubst++] = p; - } - } - if( p ){ - editDist3FromStringDelete(pStr); - pStr = 0; - break; - } - } - return pStr; + const EditDist3Lang *pLang, + const char *z, + int n +) +{ + EditDist3FromString *pStr; + EditDist3Cost *p; + int i; + + if( z==0 ) return 0; + if( n<0 ) n = (int)strlen(z); + pStr = sqlite3_malloc64( sizeof(*pStr) + sizeof(pStr->a[0])*n + n + 1 ); + if( pStr==0 ) return 0; + pStr->a = (EditDist3From*)&pStr[1]; + memset(pStr->a, 0, sizeof(pStr->a[0])*n); + pStr->n = n; + pStr->z = (char*)&pStr->a[n]; + memcpy(pStr->z, z, n+1); + if( n && z[n-1]=='*' ) { + pStr->isPrefix = 1; + n--; + pStr->n--; + pStr->z[n] = 0; + } else { + pStr->isPrefix = 0; + } + + for(i=0; ia[i]; + memset(pFrom, 0, sizeof(*pFrom)); + pFrom->nByte = utf8Len((unsigned char)z[i], n-i); + for(p=pLang->pCost; p; p=p->pNext) { + EditDist3Cost **apNew; + if( i+p->nFrom>n ) continue; + if( matchFrom(p, z+i, n-i)==0 ) continue; + if( p->nTo==0 ) { + apNew = sqlite3_realloc64(pFrom->apDel, + sizeof(*apNew)*(pFrom->nDel+1)); + if( apNew==0 ) break; + pFrom->apDel = apNew; + apNew[pFrom->nDel++] = p; + } else { + apNew = sqlite3_realloc64(pFrom->apSubst, + sizeof(*apNew)*(pFrom->nSubst+1)); + if( apNew==0 ) break; + pFrom->apSubst = apNew; + apNew[pFrom->nSubst++] = p; + } + } + if( p ) { + editDist3FromStringDelete(pStr); + pStr = 0; + break; + } + } + return pStr; } /* @@ -953,20 +984,21 @@ static EditDist3FromString *editDist3FromStringNew( ** and m[j]+iCost. */ static void updateCost( - unsigned int *m, - int i, - int j, - int iCost -){ - unsigned int b; - assert( iCost>=0 ); - assert( iCost<10000 ); - b = m[j] + iCost; - if( b=0 ); + assert( iCost<10000 ); + b = m[j] + iCost; + if( bpCost; p; p=p->pNext){ - EditDist3Cost **apNew; - if( p->nFrom>0 ) break; - if( i2+p->nTo>n2 ) continue; - if( p->a[0]>z2[i2] ) break; - if( matchTo(p, z2+i2, n2-i2)==0 ) continue; - a2[i2].nIns++; - apNew = sqlite3_realloc64(a2[i2].apIns, sizeof(*apNew)*a2[i2].nIns); - if( apNew==0 ){ - res = -1; /* Out of memory */ - goto editDist3Abort; - } - a2[i2].apIns = apNew; - a2[i2].apIns[a2[i2].nIns-1] = p; - } - } - - /* Prepare to compute the minimum edit distance */ - szRow = f.n+1; - memset(m, 0x01, (n2+1)*szRow*sizeof(m[0])); - m[0] = 0; - - /* First fill in the top-row of the matrix with FROM deletion costs */ - for(i1=0; i1iDelCost); - for(k=0; knFrom, i1, p->iCost); - } - } - - /* Fill in all subsequent rows, top-to-bottom, left-to-right */ - for(i2=0; i2iInsCost); - for(k=0; knTo), rxp, p->iCost); - } - for(i1=0; i1iDelCost); - for(k=0; knFrom, cxp, p->iCost); - } - updateCost(m, cx, cxu, pLang->iInsCost); - if( matchFromTo(&f, i1, z2+i2, n2-i2) ){ - updateCost(m, cx, cxd, 0); - } - updateCost(m, cx, cxd, pLang->iSubCost); - for(k=0; knFrom+szRow*p->nTo, cxd, p->iCost); - } - } - } - } + EditDist3FromString *pFrom, /* The FROM string */ + const char *z2, /* The TO string */ + int n2, /* Length of the TO string */ + const EditDist3Lang *pLang, /* Edit weights for a particular language ID */ + int *pnMatch /* OUT: Characters in matched prefix */ +) +{ + int k, n; + int i1, b1; + int i2, b2; + EditDist3FromString f = *pFrom; + EditDist3To *a2; + unsigned int *m; + unsigned int *pToFree; + int szRow; + EditDist3Cost *p; + int res; + sqlite3_uint64 nByte; + unsigned int stackSpace[SQLITE_SPELLFIX_STACKALLOC_SZ/sizeof(unsigned int)]; + + /* allocate the Wagner matrix and the aTo[] array for the TO string */ + n = (f.n+1)*(n2+1); + n = (n+1)&~1; + nByte = n*sizeof(m[0]) + sizeof(a2[0])*n2; + if( nByte<=sizeof(stackSpace) ) { + m = stackSpace; + pToFree = 0; + } else { + m = pToFree = sqlite3_malloc64( nByte ); + if( m==0 ) return -1; /* Out of memory */ + } + a2 = (EditDist3To*)&m[n]; + memset(a2, 0, sizeof(a2[0])*n2); + + /* Fill in the a1[] matrix for all characters of the TO string */ + for(i2=0; i2pCost; p; p=p->pNext) { + EditDist3Cost **apNew; + if( p->nFrom>0 ) break; + if( i2+p->nTo>n2 ) continue; + if( p->a[0]>z2[i2] ) break; + if( matchTo(p, z2+i2, n2-i2)==0 ) continue; + a2[i2].nIns++; + apNew = sqlite3_realloc64(a2[i2].apIns, sizeof(*apNew)*a2[i2].nIns); + if( apNew==0 ) { + res = -1; /* Out of memory */ + goto editDist3Abort; + } + a2[i2].apIns = apNew; + a2[i2].apIns[a2[i2].nIns-1] = p; + } + } + + /* Prepare to compute the minimum edit distance */ + szRow = f.n+1; + memset(m, 0x01, (n2+1)*szRow*sizeof(m[0])); + m[0] = 0; + + /* First fill in the top-row of the matrix with FROM deletion costs */ + for(i1=0; i1iDelCost); + for(k=0; knFrom, i1, p->iCost); + } + } + + /* Fill in all subsequent rows, top-to-bottom, left-to-right */ + for(i2=0; i2iInsCost); + for(k=0; knTo), rxp, p->iCost); + } + for(i1=0; i1iDelCost); + for(k=0; knFrom, cxp, p->iCost); + } + updateCost(m, cx, cxu, pLang->iInsCost); + if( matchFromTo(&f, i1, z2+i2, n2-i2) ) { + updateCost(m, cx, cxd, 0); + } + updateCost(m, cx, cxd, pLang->iSubCost); + for(k=0; knFrom+szRow*p->nTo, cxd, p->iCost); + } + } + } + } #if 0 /* Enable for debugging */ - printf(" ^"); - for(i1=0; i19999 ) printf(" ****"); - else printf(" %4d", v); - } - printf("\n"); - for(i2=0; i29999 ) printf(" ****"); - else printf(" %4d", v); + printf(" ^"); + for(i1=0; i19999 ) printf(" ****"); + else printf(" %4d", v); } printf("\n"); - } + for(i2=0; i29999 ) printf(" ****"); + else printf(" %4d", v); + } + printf("\n"); + } #endif - /* Free memory allocations and return the result */ - res = (int)m[szRow*(n2+1)-1]; - n = n2; - if( f.isPrefix ){ - for(i2=1; i2<=n2; i2++){ - int b = m[szRow*i2-1]; - if( b<=res ){ - res = b; - n = i2 - 1; - } - } - } - if( pnMatch ){ - int nExtra = 0; - for(k=0; knLang; i++){ - if( pConfig->a[i].iLang==iLang ) return &pConfig->a[i]; - } - return &editDist3Lang; + EditDist3Config *pConfig, + int iLang +) +{ + int i; + for(i=0; inLang; i++) { + if( pConfig->a[i].iLang==iLang ) return &pConfig->a[i]; + } + return &editDist3Lang; } /* @@ -1168,66 +1202,68 @@ static const EditDist3Lang *editDist3FindLang( ** The second form loads edit weights into memory from a table. */ static void editDist3SqlFunc( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - EditDist3Config *pConfig = (EditDist3Config*)sqlite3_user_data(context); - sqlite3 *db = sqlite3_context_db_handle(context); - int rc; - if( argc==1 ){ - const char *zTable = (const char*)sqlite3_value_text(argv[0]); - rc = editDist3ConfigLoad(pConfig, db, zTable); - if( rc ) sqlite3_result_error_code(context, rc); - }else{ - const char *zA = (const char*)sqlite3_value_text(argv[0]); - const char *zB = (const char*)sqlite3_value_text(argv[1]); - int nA = sqlite3_value_bytes(argv[0]); - int nB = sqlite3_value_bytes(argv[1]); - int iLang = argc==3 ? sqlite3_value_int(argv[2]) : 0; - const EditDist3Lang *pLang = editDist3FindLang(pConfig, iLang); - EditDist3FromString *pFrom; - int dist; - - pFrom = editDist3FromStringNew(pLang, zA, nA); - if( pFrom==0 ){ - sqlite3_result_error_nomem(context); - return; - } - dist = editDist3Core(pFrom, zB, nB, pLang, 0); - editDist3FromStringDelete(pFrom); - if( dist==(-1) ){ - sqlite3_result_error_nomem(context); - }else{ - sqlite3_result_int(context, dist); - } - } + sqlite3_context *context, + int argc, + sqlite3_value **argv +) +{ + EditDist3Config *pConfig = (EditDist3Config*)sqlite3_user_data(context); + sqlite3 *db = sqlite3_context_db_handle(context); + int rc; + if( argc==1 ) { + const char *zTable = (const char*)sqlite3_value_text(argv[0]); + rc = editDist3ConfigLoad(pConfig, db, zTable); + if( rc ) sqlite3_result_error_code(context, rc); + } else { + const char *zA = (const char*)sqlite3_value_text(argv[0]); + const char *zB = (const char*)sqlite3_value_text(argv[1]); + int nA = sqlite3_value_bytes(argv[0]); + int nB = sqlite3_value_bytes(argv[1]); + int iLang = argc==3 ? sqlite3_value_int(argv[2]) : 0; + const EditDist3Lang *pLang = editDist3FindLang(pConfig, iLang); + EditDist3FromString *pFrom; + int dist; + + pFrom = editDist3FromStringNew(pLang, zA, nA); + if( pFrom==0 ) { + sqlite3_result_error_nomem(context); + return; + } + dist = editDist3Core(pFrom, zB, nB, pLang, 0); + editDist3FromStringDelete(pFrom); + if( dist==(-1) ) { + sqlite3_result_error_nomem(context); + } else { + sqlite3_result_int(context, dist); + } + } } /* ** Register the editDist3 function with SQLite */ -static int editDist3Install(sqlite3 *db){ - int rc; - EditDist3Config *pConfig = sqlite3_malloc64( sizeof(*pConfig) ); - if( pConfig==0 ) return SQLITE_NOMEM; - memset(pConfig, 0, sizeof(*pConfig)); - rc = sqlite3_create_function_v2(db, "editdist3", - 2, SQLITE_UTF8|SQLITE_DETERMINISTIC, pConfig, - editDist3SqlFunc, 0, 0, 0); - if( rc==SQLITE_OK ){ +static int editDist3Install(sqlite3 *db) +{ + int rc; + EditDist3Config *pConfig = sqlite3_malloc64( sizeof(*pConfig) ); + if( pConfig==0 ) return SQLITE_NOMEM; + memset(pConfig, 0, sizeof(*pConfig)); rc = sqlite3_create_function_v2(db, "editdist3", - 3, SQLITE_UTF8|SQLITE_DETERMINISTIC, pConfig, - editDist3SqlFunc, 0, 0, 0); - } - if( rc==SQLITE_OK ){ - rc = sqlite3_create_function_v2(db, "editdist3", - 1, SQLITE_UTF8|SQLITE_DETERMINISTIC, pConfig, - editDist3SqlFunc, 0, 0, editDist3ConfigDelete); - }else{ - sqlite3_free(pConfig); - } - return rc; + 2, SQLITE_UTF8|SQLITE_DETERMINISTIC, pConfig, + editDist3SqlFunc, 0, 0, 0); + if( rc==SQLITE_OK ) { + rc = sqlite3_create_function_v2(db, "editdist3", + 3, SQLITE_UTF8|SQLITE_DETERMINISTIC, pConfig, + editDist3SqlFunc, 0, 0, 0); + } + if( rc==SQLITE_OK ) { + rc = sqlite3_create_function_v2(db, "editdist3", + 1, SQLITE_UTF8|SQLITE_DETERMINISTIC, pConfig, + editDist3SqlFunc, 0, 0, editDist3ConfigDelete); + } else { + sqlite3_free(pConfig); + } + return rc; } /* End configurable cost unicode edit distance routines ****************************************************************************** @@ -1241,62 +1277,64 @@ static int editDist3Install(sqlite3 *db){ ** a multi-byte UTF8 character. */ static const unsigned char sqlite3Utf8Trans1[] = { - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, - 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, - 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, - 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, - 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x00, 0x00, }; #endif /* ** Return the value of the first UTF-8 character in the string. */ -static int utf8Read(const unsigned char *z, int n, int *pSize){ - int c, i; - - /* All callers to this routine (in the current implementation) - ** always have n>0. */ - if( NEVER(n==0) ){ - c = i = 0; - }else{ - c = z[0]; - i = 1; - if( c>=0xc0 ){ - c = sqlite3Utf8Trans1[c-0xc0]; - while( i0. */ + if( NEVER(n==0) ) { + c = i = 0; + } else { + c = z[0]; + i = 1; + if( c>=0xc0 ) { + c = sqlite3Utf8Trans1[c-0xc0]; + while( i0 ){ - c = utf8Read(zIn, nIn, &sz); - zIn += sz; - nIn -= sz; - if( c<=127 ){ - zOut[nOut++] = (unsigned char)c; - }else{ - int xTop, xBtm, x; - const Transliteration *tbl = spellfixFindTranslit(c, &xTop); - xBtm = 0; - while( xTop>=xBtm ){ - x = (xTop + xBtm)/2; - if( tbl[x].cFrom==c ){ - zOut[nOut++] = tbl[x].cTo0; - if( tbl[x].cTo1 ){ - zOut[nOut++] = tbl[x].cTo1; - if( tbl[x].cTo2 ){ - zOut[nOut++] = tbl[x].cTo2; - if( tbl[x].cTo3 ){ - zOut[nOut++] = tbl[x].cTo3; + int c, sz, nOut; + if( zOut==0 ) return 0; + nOut = 0; + while( nIn>0 ) { + c = utf8Read(zIn, nIn, &sz); + zIn += sz; + nIn -= sz; + if( c<=127 ) { + zOut[nOut++] = (unsigned char)c; + } else { + int xTop, xBtm, x; + const Transliteration *tbl = spellfixFindTranslit(c, &xTop); + xBtm = 0; + while( xTop>=xBtm ) { + x = (xTop + xBtm)/2; + if( tbl[x].cFrom==c ) { + zOut[nOut++] = tbl[x].cTo0; + if( tbl[x].cTo1 ) { + zOut[nOut++] = tbl[x].cTo1; + if( tbl[x].cTo2 ) { + zOut[nOut++] = tbl[x].cTo2; + if( tbl[x].cTo3 ) { + zOut[nOut++] = tbl[x].cTo3; #ifdef SQLITE_SPELLFIX_5BYTE_MAPPINGS - if( tbl[x].cTo4 ){ - zOut[nOut++] = tbl[x].cTo4; - } + if( tbl[x].cTo4 ) { + zOut[nOut++] = tbl[x].cTo4; + } #endif /* SQLITE_SPELLFIX_5BYTE_MAPPINGS */ - } + } + } + } + c = 0; + break; + } else if( tbl[x].cFrom>c ) { + xTop = x-1; + } else { + xBtm = x+1; + } } - } - c = 0; - break; - }else if( tbl[x].cFrom>c ){ - xTop = x-1; - }else{ - xBtm = x+1; - } - } - if( c ) zOut[nOut++] = '?'; - } - } - zOut[nOut] = 0; - return zOut; + if( c ) zOut[nOut++] = '?'; + } + } + zOut[nOut] = 0; + return zOut; } /* @@ -1768,65 +1808,66 @@ static unsigned char *transliterate(const unsigned char *zIn, int nIn){ ** Or, if the transliteration of the input string is less than nTrans ** bytes in size, return the number of characters in the input string. */ -static int translen_to_charlen(const char *zIn, int nIn, int nTrans){ - int i, c, sz, nOut; - int nChar; - - i = nOut = 0; - for(nChar=0; i=128 ){ - int xTop, xBtm, x; - const Transliteration *tbl = spellfixFindTranslit(c, &xTop); - xBtm = 0; - while( xTop>=xBtm ){ - x = (xTop + xBtm)/2; - if( tbl[x].cFrom==c ){ - if( tbl[x].cTo1 ){ - nOut++; - if( tbl[x].cTo2 ){ - nOut++; - if( tbl[x].cTo3 ){ - nOut++; - } +static int translen_to_charlen(const char *zIn, int nIn, int nTrans) +{ + int i, c, sz, nOut; + int nChar; + + i = nOut = 0; + for(nChar=0; i=128 ) { + int xTop, xBtm, x; + const Transliteration *tbl = spellfixFindTranslit(c, &xTop); + xBtm = 0; + while( xTop>=xBtm ) { + x = (xTop + xBtm)/2; + if( tbl[x].cFrom==c ) { + if( tbl[x].cTo1 ) { + nOut++; + if( tbl[x].cTo2 ) { + nOut++; + if( tbl[x].cTo3 ) { + nOut++; + } + } + } + break; + } else if( tbl[x].cFrom>c ) { + xTop = x-1; + } else { + xBtm = x+1; + } } - } - break; - }else if( tbl[x].cFrom>c ){ - xTop = x-1; - }else{ - xBtm = x+1; } - } } - } - return nChar; + return nChar; } - /* ** spellfix1_translit(X) ** -** Convert a string that contains non-ASCII Roman characters into +** Convert a string that contains non-ASCII Roman characters into ** pure ASCII. */ static void transliterateSqlFunc( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - const unsigned char *zIn = sqlite3_value_text(argv[0]); - int nIn = sqlite3_value_bytes(argv[0]); - unsigned char *zOut = transliterate(zIn, nIn); - if( zOut==0 ){ - sqlite3_result_error_nomem(context); - }else{ - sqlite3_result_text(context, (char*)zOut, -1, sqlite3_free); - } + sqlite3_context *context, + int argc, + sqlite3_value **argv +) +{ + const unsigned char *zIn = sqlite3_value_text(argv[0]); + int nIn = sqlite3_value_bytes(argv[0]); + unsigned char *zOut = transliterate(zIn, nIn); + if( zOut==0 ) { + sqlite3_result_error_nomem(context); + } else { + sqlite3_result_text(context, (char*)zOut, -1, sqlite3_free); + } } /* @@ -1846,53 +1887,68 @@ static void transliterateSqlFunc( ** from any of the above scripts. */ static void scriptCodeSqlFunc( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - const unsigned char *zIn = sqlite3_value_text(argv[0]); - int nIn = sqlite3_value_bytes(argv[0]); - int c, sz; - int scriptMask = 0; - int res; - int seenDigit = 0; + sqlite3_context *context, + int argc, + sqlite3_value **argv +) +{ + const unsigned char *zIn = sqlite3_value_text(argv[0]); + int nIn = sqlite3_value_bytes(argv[0]); + int c, sz; + int scriptMask = 0; + int res; + int seenDigit = 0; # define SCRIPT_LATIN 0x0001 # define SCRIPT_CYRILLIC 0x0002 # define SCRIPT_GREEK 0x0004 # define SCRIPT_HEBREW 0x0008 # define SCRIPT_ARABIC 0x0010 - while( nIn>0 ){ - c = utf8Read(zIn, nIn, &sz); - zIn += sz; - nIn -= sz; - if( c<0x02af ){ - if( c>=0x80 || midClass[c&0x7f]='0' && c<='9' ){ - seenDigit = 1; - } - }else if( c>=0x0400 && c<=0x04ff ){ - scriptMask |= SCRIPT_CYRILLIC; - }else if( c>=0x0386 && c<=0x03ce ){ - scriptMask |= SCRIPT_GREEK; - }else if( c>=0x0590 && c<=0x05ff ){ - scriptMask |= SCRIPT_HEBREW; - }else if( c>=0x0600 && c<=0x06ff ){ - scriptMask |= SCRIPT_ARABIC; - } - } - if( scriptMask==0 && seenDigit ) scriptMask = SCRIPT_LATIN; - switch( scriptMask ){ - case 0: res = 999; break; - case SCRIPT_LATIN: res = 215; break; - case SCRIPT_CYRILLIC: res = 220; break; - case SCRIPT_GREEK: res = 200; break; - case SCRIPT_HEBREW: res = 125; break; - case SCRIPT_ARABIC: res = 160; break; - default: res = 998; break; - } - sqlite3_result_int(context, res); + while( nIn>0 ) { + c = utf8Read(zIn, nIn, &sz); + zIn += sz; + nIn -= sz; + if( c<0x02af ) { + if( c>=0x80 || midClass[c&0x7f]='0' && c<='9' ) { + seenDigit = 1; + } + } else if( c>=0x0400 && c<=0x04ff ) { + scriptMask |= SCRIPT_CYRILLIC; + } else if( c>=0x0386 && c<=0x03ce ) { + scriptMask |= SCRIPT_GREEK; + } else if( c>=0x0590 && c<=0x05ff ) { + scriptMask |= SCRIPT_HEBREW; + } else if( c>=0x0600 && c<=0x06ff ) { + scriptMask |= SCRIPT_ARABIC; + } + } + if( scriptMask==0 && seenDigit ) scriptMask = SCRIPT_LATIN; + switch( scriptMask ) { + case 0: + res = 999; + break; + case SCRIPT_LATIN: + res = 215; + break; + case SCRIPT_CYRILLIC: + res = 220; + break; + case SCRIPT_GREEK: + res = 200; + break; + case SCRIPT_HEBREW: + res = 125; + break; + case SCRIPT_ARABIC: + res = 160; + break; + default: + res = 998; + break; + } + sqlite3_result_int(context, res); } /* End transliterate @@ -1912,37 +1968,37 @@ typedef struct spellfix1_cursor spellfix1_cursor; /* Fuzzy-search virtual table object */ struct spellfix1_vtab { - sqlite3_vtab base; /* Base class - must be first */ - sqlite3 *db; /* Database connection */ - char *zDbName; /* Name of database holding this table */ - char *zTableName; /* Name of the virtual table */ - char *zCostTable; /* Table holding edit-distance cost numbers */ - EditDist3Config *pConfig3; /* Parsed edit distance costs */ + sqlite3_vtab base; /* Base class - must be first */ + sqlite3 *db; /* Database connection */ + char *zDbName; /* Name of database holding this table */ + char *zTableName; /* Name of the virtual table */ + char *zCostTable; /* Table holding edit-distance cost numbers */ + EditDist3Config *pConfig3; /* Parsed edit distance costs */ }; /* Fuzzy-search cursor object */ struct spellfix1_cursor { - sqlite3_vtab_cursor base; /* Base class - must be first */ - spellfix1_vtab *pVTab; /* The table to which this cursor belongs */ - char *zPattern; /* rhs of MATCH clause */ - int idxNum; /* idxNum value passed to xFilter() */ - int nRow; /* Number of rows of content */ - int nAlloc; /* Number of allocated rows */ - int iRow; /* Current row of content */ - int iLang; /* Value of the langid= constraint */ - int iTop; /* Value of the top= constraint */ - int iScope; /* Value of the scope= constraint */ - int nSearch; /* Number of vocabulary items checked */ - sqlite3_stmt *pFullScan; /* Shadow query for a full table scan */ - struct spellfix1_row { /* For each row of content */ - sqlite3_int64 iRowid; /* Rowid for this row */ - char *zWord; /* Text for this row */ - int iRank; /* Rank for this row */ - int iDistance; /* Distance from pattern for this row */ - int iScore; /* Score for sorting */ - int iMatchlen; /* Value of matchlen column (or -1) */ - char zHash[SPELLFIX_MX_HASH]; /* the phonehash used for this match */ - } *a; + sqlite3_vtab_cursor base; /* Base class - must be first */ + spellfix1_vtab *pVTab; /* The table to which this cursor belongs */ + char *zPattern; /* rhs of MATCH clause */ + int idxNum; /* idxNum value passed to xFilter() */ + int nRow; /* Number of rows of content */ + int nAlloc; /* Number of allocated rows */ + int iRow; /* Current row of content */ + int iLang; /* Value of the langid= constraint */ + int iTop; /* Value of the top= constraint */ + int iScope; /* Value of the scope= constraint */ + int nSearch; /* Number of vocabulary items checked */ + sqlite3_stmt *pFullScan; /* Shadow query for a full table scan */ + struct spellfix1_row { /* For each row of content */ + sqlite3_int64 iRowid; /* Rowid for this row */ + char *zWord; /* Text for this row */ + int iRank; /* Rank for this row */ + int iDistance; /* Distance from pattern for this row */ + int iScore; /* Score for sorting */ + int iMatchlen; /* Value of matchlen column (or -1) */ + char zHash[SPELLFIX_MX_HASH]; /* the phonehash used for this match */ + } *a; }; /* @@ -1953,85 +2009,91 @@ struct spellfix1_cursor { ** If *pRc is initially non-zero then this routine is a no-op. */ static void spellfix1DbExec( - int *pRc, /* Success code */ - sqlite3 *db, /* Database in which to run SQL */ - const char *zFormat, /* Format string for SQL */ - ... /* Arguments to the format string */ -){ - va_list ap; - char *zSql; - if( *pRc ) return; - va_start(ap, zFormat); - zSql = sqlite3_vmprintf(zFormat, ap); - va_end(ap); - if( zSql==0 ){ - *pRc = SQLITE_NOMEM; - }else{ - *pRc = sqlite3_exec(db, zSql, 0, 0, 0); - sqlite3_free(zSql); - } + int *pRc, /* Success code */ + sqlite3 *db, /* Database in which to run SQL */ + const char *zFormat, /* Format string for SQL */ + ... /* Arguments to the format string */ +) +{ + va_list ap; + char *zSql; + if( *pRc ) return; + va_start(ap, zFormat); + zSql = sqlite3_vmprintf(zFormat, ap); + va_end(ap); + if( zSql==0 ) { + *pRc = SQLITE_NOMEM; + } else { + *pRc = sqlite3_exec(db, zSql, 0, 0, 0); + sqlite3_free(zSql); + } } /* ** xDisconnect/xDestroy method for the fuzzy-search module. */ -static int spellfix1Uninit(int isDestroy, sqlite3_vtab *pVTab){ - spellfix1_vtab *p = (spellfix1_vtab*)pVTab; - int rc = SQLITE_OK; - if( isDestroy ){ - sqlite3 *db = p->db; - spellfix1DbExec(&rc, db, "DROP TABLE IF EXISTS \"%w\".\"%w_vocab\"", - p->zDbName, p->zTableName); - } - if( rc==SQLITE_OK ){ - sqlite3_free(p->zTableName); - editDist3ConfigDelete(p->pConfig3); - sqlite3_free(p->zCostTable); - sqlite3_free(p); - } - return rc; +static int spellfix1Uninit(int isDestroy, sqlite3_vtab *pVTab) +{ + spellfix1_vtab *p = (spellfix1_vtab*)pVTab; + int rc = SQLITE_OK; + if( isDestroy ) { + sqlite3 *db = p->db; + spellfix1DbExec(&rc, db, "DROP TABLE IF EXISTS \"%w\".\"%w_vocab\"", + p->zDbName, p->zTableName); + } + if( rc==SQLITE_OK ) { + sqlite3_free(p->zTableName); + editDist3ConfigDelete(p->pConfig3); + sqlite3_free(p->zCostTable); + sqlite3_free(p); + } + return rc; } -static int spellfix1Disconnect(sqlite3_vtab *pVTab){ - return spellfix1Uninit(0, pVTab); +static int spellfix1Disconnect(sqlite3_vtab *pVTab) +{ + return spellfix1Uninit(0, pVTab); } -static int spellfix1Destroy(sqlite3_vtab *pVTab){ - return spellfix1Uninit(1, pVTab); +static int spellfix1Destroy(sqlite3_vtab *pVTab) +{ + return spellfix1Uninit(1, pVTab); } /* ** Make a copy of a string. Remove leading and trailing whitespace ** and dequote it. */ -static char *spellfix1Dequote(const char *zIn){ - char *zOut; - int i, j; - char c; - while( isspace((unsigned char)zIn[0]) ) zIn++; - zOut = sqlite3_mprintf("%s", zIn); - if( zOut==0 ) return 0; - i = (int)strlen(zOut); +static char *spellfix1Dequote(const char *zIn) +{ + char *zOut; + int i, j; + char c; + while( isspace((unsigned char)zIn[0]) ) zIn++; + zOut = sqlite3_mprintf("%s", zIn); + if( zOut==0 ) return 0; + i = (int)strlen(zOut); #if 0 /* The parser will never leave spaces at the end */ - while( i>0 && isspace(zOut[i-1]) ){ i--; } + while( i>0 && isspace(zOut[i-1]) ) { + i--; + } #endif - zOut[i] = 0; - c = zOut[0]; - if( c=='\'' || c=='"' ){ - for(i=1, j=0; ALWAYS(zOut[i]); i++){ - zOut[j++] = zOut[i]; - if( zOut[i]==c ){ - if( zOut[i+1]==c ){ - i++; - }else{ - zOut[j-1] = 0; - break; - } - } - } - } - return zOut; + zOut[i] = 0; + c = zOut[0]; + if( c=='\'' || c=='"' ) { + for(i=1, j=0; ALWAYS(zOut[i]); i++) { + zOut[j++] = zOut[i]; + if( zOut[i]==c ) { + if( zOut[i+1]==c ) { + i++; + } else { + zOut[j-1] = 0; + break; + } + } + } + } + return zOut; } - /* ** xConnect/xCreate method for the spellfix1 module. Arguments are: ** @@ -2041,41 +2103,42 @@ static char *spellfix1Dequote(const char *zIn){ ** argv[3].. -> optional arguments (i.e. "edit_cost_table" parameter) */ static int spellfix1Init( - int isCreate, - sqlite3 *db, - void *pAux, - int argc, const char *const*argv, - sqlite3_vtab **ppVTab, - char **pzErr -){ - spellfix1_vtab *pNew = 0; - /* const char *zModule = argv[0]; // not used */ - const char *zDbName = argv[1]; - const char *zTableName = argv[2]; - int nDbName; - int rc = SQLITE_OK; - int i; - - nDbName = (int)strlen(zDbName); - pNew = sqlite3_malloc64( sizeof(*pNew) + nDbName + 1); - if( pNew==0 ){ - rc = SQLITE_NOMEM; - }else{ - memset(pNew, 0, sizeof(*pNew)); - pNew->zDbName = (char*)&pNew[1]; - memcpy(pNew->zDbName, zDbName, nDbName+1); - pNew->zTableName = sqlite3_mprintf("%s", zTableName); - pNew->db = db; - if( pNew->zTableName==0 ){ - rc = SQLITE_NOMEM; - }else{ - sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS); - rc = sqlite3_declare_vtab(db, - "CREATE TABLE x(word,rank,distance,langid, " - "score, matchlen, phonehash HIDDEN, " - "top HIDDEN, scope HIDDEN, srchcnt HIDDEN, " - "soundslike HIDDEN, command HIDDEN)" - ); + int isCreate, + sqlite3 *db, + void *pAux, + int argc, const char *const*argv, + sqlite3_vtab **ppVTab, + char **pzErr +) +{ + spellfix1_vtab *pNew = 0; + /* const char *zModule = argv[0]; // not used */ + const char *zDbName = argv[1]; + const char *zTableName = argv[2]; + int nDbName; + int rc = SQLITE_OK; + int i; + + nDbName = (int)strlen(zDbName); + pNew = sqlite3_malloc64( sizeof(*pNew) + nDbName + 1); + if( pNew==0 ) { + rc = SQLITE_NOMEM; + } else { + memset(pNew, 0, sizeof(*pNew)); + pNew->zDbName = (char*)&pNew[1]; + memcpy(pNew->zDbName, zDbName, nDbName+1); + pNew->zTableName = sqlite3_mprintf("%s", zTableName); + pNew->db = db; + if( pNew->zTableName==0 ) { + rc = SQLITE_NOMEM; + } else { + sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS); + rc = sqlite3_declare_vtab(db, + "CREATE TABLE x(word,rank,distance,langid, " + "score, matchlen, phonehash HIDDEN, " + "top HIDDEN, scope HIDDEN, srchcnt HIDDEN, " + "soundslike HIDDEN, command HIDDEN)" + ); #define SPELLFIX_COL_WORD 0 #define SPELLFIX_COL_RANK 1 #define SPELLFIX_COL_DISTANCE 2 @@ -2088,113 +2151,117 @@ static int spellfix1Init( #define SPELLFIX_COL_SRCHCNT 9 #define SPELLFIX_COL_SOUNDSLIKE 10 #define SPELLFIX_COL_COMMAND 11 + } + if( rc==SQLITE_OK && isCreate ) { + spellfix1DbExec(&rc, db, + "CREATE TABLE IF NOT EXISTS \"%w\".\"%w_vocab\"(\n" + " id INTEGER PRIMARY KEY,\n" + " rank INT,\n" + " langid INT,\n" + " word TEXT,\n" + " k1 TEXT,\n" + " k2 TEXT\n" + ");\n", + zDbName, zTableName + ); + spellfix1DbExec(&rc, db, + "CREATE INDEX IF NOT EXISTS \"%w\".\"%w_vocab_index_langid_k2\" " + "ON \"%w_vocab\"(langid,k2);", + zDbName, zTableName, zTableName + ); + } + for(i=3; rc==SQLITE_OK && ibase); + } else { + *ppVTab = (sqlite3_vtab *)pNew; } - if( rc==SQLITE_OK && isCreate ){ - spellfix1DbExec(&rc, db, - "CREATE TABLE IF NOT EXISTS \"%w\".\"%w_vocab\"(\n" - " id INTEGER PRIMARY KEY,\n" - " rank INT,\n" - " langid INT,\n" - " word TEXT,\n" - " k1 TEXT,\n" - " k2 TEXT\n" - ");\n", - zDbName, zTableName - ); - spellfix1DbExec(&rc, db, - "CREATE INDEX IF NOT EXISTS \"%w\".\"%w_vocab_index_langid_k2\" " - "ON \"%w_vocab\"(langid,k2);", - zDbName, zTableName, zTableName - ); - } - for(i=3; rc==SQLITE_OK && ibase); - }else{ - *ppVTab = (sqlite3_vtab *)pNew; - } - return rc; + return rc; } /* ** The xConnect and xCreate methods */ static int spellfix1Connect( - sqlite3 *db, - void *pAux, - int argc, const char *const*argv, - sqlite3_vtab **ppVTab, - char **pzErr -){ - return spellfix1Init(0, db, pAux, argc, argv, ppVTab, pzErr); + sqlite3 *db, + void *pAux, + int argc, const char *const*argv, + sqlite3_vtab **ppVTab, + char **pzErr +) +{ + return spellfix1Init(0, db, pAux, argc, argv, ppVTab, pzErr); } static int spellfix1Create( - sqlite3 *db, - void *pAux, - int argc, const char *const*argv, - sqlite3_vtab **ppVTab, - char **pzErr -){ - return spellfix1Init(1, db, pAux, argc, argv, ppVTab, pzErr); + sqlite3 *db, + void *pAux, + int argc, const char *const*argv, + sqlite3_vtab **ppVTab, + char **pzErr +) +{ + return spellfix1Init(1, db, pAux, argc, argv, ppVTab, pzErr); } /* ** Clear all of the content from a cursor. */ -static void spellfix1ResetCursor(spellfix1_cursor *pCur){ - int i; - for(i=0; inRow; i++){ - sqlite3_free(pCur->a[i].zWord); - } - pCur->nRow = 0; - pCur->iRow = 0; - pCur->nSearch = 0; - if( pCur->pFullScan ){ - sqlite3_finalize(pCur->pFullScan); - pCur->pFullScan = 0; - } +static void spellfix1ResetCursor(spellfix1_cursor *pCur) +{ + int i; + for(i=0; inRow; i++) { + sqlite3_free(pCur->a[i].zWord); + } + pCur->nRow = 0; + pCur->iRow = 0; + pCur->nSearch = 0; + if( pCur->pFullScan ) { + sqlite3_finalize(pCur->pFullScan); + pCur->pFullScan = 0; + } } /* ** Resize the cursor to hold up to N rows of content */ -static void spellfix1ResizeCursor(spellfix1_cursor *pCur, int N){ - struct spellfix1_row *aNew; - assert( N>=pCur->nRow ); - aNew = sqlite3_realloc64(pCur->a, sizeof(pCur->a[0])*N); - if( aNew==0 && N>0 ){ - spellfix1ResetCursor(pCur); - sqlite3_free(pCur->a); - pCur->nAlloc = 0; - pCur->a = 0; - }else{ - pCur->nAlloc = N; - pCur->a = aNew; - } +static void spellfix1ResizeCursor(spellfix1_cursor *pCur, int N) +{ + struct spellfix1_row *aNew; + assert( N>=pCur->nRow ); + aNew = sqlite3_realloc64(pCur->a, sizeof(pCur->a[0])*N); + if( aNew==0 && N>0 ) { + spellfix1ResetCursor(pCur); + sqlite3_free(pCur->a); + pCur->nAlloc = 0; + pCur->a = 0; + } else { + pCur->nAlloc = N; + pCur->a = aNew; + } } - /* ** Close a fuzzy-search cursor. */ -static int spellfix1Close(sqlite3_vtab_cursor *cur){ - spellfix1_cursor *pCur = (spellfix1_cursor *)cur; - spellfix1ResetCursor(pCur); - spellfix1ResizeCursor(pCur, 0); - sqlite3_free(pCur->zPattern); - sqlite3_free(pCur); - return SQLITE_OK; +static int spellfix1Close(sqlite3_vtab_cursor *cur) +{ + spellfix1_cursor *pCur = (spellfix1_cursor *)cur; + spellfix1ResetCursor(pCur); + spellfix1ResizeCursor(pCur, 0); + sqlite3_free(pCur->zPattern); + sqlite3_free(pCur); + return SQLITE_OK; } #define SPELLFIX_IDXNUM_MATCH 0x01 /* word MATCH $str */ @@ -2214,149 +2281,153 @@ static int spellfix1Close(sqlite3_vtab_cursor *cur){ ** filter.argv[*] values contains $str, $langid, $top, $scope and $rowid ** if specified and in that order. */ -static int spellfix1BestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){ - int iPlan = 0; - int iLangTerm = -1; - int iTopTerm = -1; - int iScopeTerm = -1; - int iDistTerm = -1; - int iRowidTerm = -1; - int i; - const struct sqlite3_index_constraint *pConstraint; - pConstraint = pIdxInfo->aConstraint; - for(i=0; inConstraint; i++, pConstraint++){ - if( pConstraint->usable==0 ) continue; - - /* Terms of the form: word MATCH $str */ - if( (iPlan & SPELLFIX_IDXNUM_MATCH)==0 - && pConstraint->iColumn==SPELLFIX_COL_WORD - && pConstraint->op==SQLITE_INDEX_CONSTRAINT_MATCH - ){ - iPlan |= SPELLFIX_IDXNUM_MATCH; - pIdxInfo->aConstraintUsage[i].argvIndex = 1; - pIdxInfo->aConstraintUsage[i].omit = 1; - } - - /* Terms of the form: langid = $langid */ - if( (iPlan & SPELLFIX_IDXNUM_LANGID)==0 - && pConstraint->iColumn==SPELLFIX_COL_LANGID - && pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ - ){ - iPlan |= SPELLFIX_IDXNUM_LANGID; - iLangTerm = i; - } - - /* Terms of the form: top = $top */ - if( (iPlan & SPELLFIX_IDXNUM_TOP)==0 - && pConstraint->iColumn==SPELLFIX_COL_TOP - && pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ - ){ - iPlan |= SPELLFIX_IDXNUM_TOP; - iTopTerm = i; - } - - /* Terms of the form: scope = $scope */ - if( (iPlan & SPELLFIX_IDXNUM_SCOPE)==0 - && pConstraint->iColumn==SPELLFIX_COL_SCOPE - && pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ - ){ - iPlan |= SPELLFIX_IDXNUM_SCOPE; - iScopeTerm = i; - } - - /* Terms of the form: distance < $dist or distance <= $dist */ - if( (iPlan & SPELLFIX_IDXNUM_DIST)==0 - && pConstraint->iColumn==SPELLFIX_COL_DISTANCE - && (pConstraint->op==SQLITE_INDEX_CONSTRAINT_LT - || pConstraint->op==SQLITE_INDEX_CONSTRAINT_LE) - ){ - if( pConstraint->op==SQLITE_INDEX_CONSTRAINT_LT ){ - iPlan |= SPELLFIX_IDXNUM_DISTLT; - }else{ - iPlan |= SPELLFIX_IDXNUM_DISTLE; - } - iDistTerm = i; - } - - /* Terms of the form: distance < $dist or distance <= $dist */ - if( (iPlan & SPELLFIX_IDXNUM_ROWID)==0 - && pConstraint->iColumn<0 - && pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ - ){ - iPlan |= SPELLFIX_IDXNUM_ROWID; - iRowidTerm = i; - } - } - if( iPlan&SPELLFIX_IDXNUM_MATCH ){ - int idx = 2; - pIdxInfo->idxNum = iPlan; - if( pIdxInfo->nOrderBy==1 - && pIdxInfo->aOrderBy[0].iColumn==SPELLFIX_COL_SCORE - && pIdxInfo->aOrderBy[0].desc==0 - ){ - pIdxInfo->orderByConsumed = 1; /* Default order by iScore */ - } - if( iPlan&SPELLFIX_IDXNUM_LANGID ){ - pIdxInfo->aConstraintUsage[iLangTerm].argvIndex = idx++; - pIdxInfo->aConstraintUsage[iLangTerm].omit = 1; - } - if( iPlan&SPELLFIX_IDXNUM_TOP ){ - pIdxInfo->aConstraintUsage[iTopTerm].argvIndex = idx++; - pIdxInfo->aConstraintUsage[iTopTerm].omit = 1; - } - if( iPlan&SPELLFIX_IDXNUM_SCOPE ){ - pIdxInfo->aConstraintUsage[iScopeTerm].argvIndex = idx++; - pIdxInfo->aConstraintUsage[iScopeTerm].omit = 1; - } - if( iPlan&SPELLFIX_IDXNUM_DIST ){ - pIdxInfo->aConstraintUsage[iDistTerm].argvIndex = idx++; - pIdxInfo->aConstraintUsage[iDistTerm].omit = 1; - } - pIdxInfo->estimatedCost = 1e5; - }else if( (iPlan & SPELLFIX_IDXNUM_ROWID) ){ - pIdxInfo->idxNum = SPELLFIX_IDXNUM_ROWID; - pIdxInfo->aConstraintUsage[iRowidTerm].argvIndex = 1; - pIdxInfo->aConstraintUsage[iRowidTerm].omit = 1; - pIdxInfo->estimatedCost = 5; - }else{ - pIdxInfo->idxNum = 0; - pIdxInfo->estimatedCost = 1e50; - } - return SQLITE_OK; +static int spellfix1BestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo) +{ + int iPlan = 0; + int iLangTerm = -1; + int iTopTerm = -1; + int iScopeTerm = -1; + int iDistTerm = -1; + int iRowidTerm = -1; + int i; + const struct sqlite3_index_constraint *pConstraint; + pConstraint = pIdxInfo->aConstraint; + for(i=0; inConstraint; i++, pConstraint++) { + if( pConstraint->usable==0 ) continue; + + /* Terms of the form: word MATCH $str */ + if( (iPlan & SPELLFIX_IDXNUM_MATCH)==0 + && pConstraint->iColumn==SPELLFIX_COL_WORD + && pConstraint->op==SQLITE_INDEX_CONSTRAINT_MATCH + ) { + iPlan |= SPELLFIX_IDXNUM_MATCH; + pIdxInfo->aConstraintUsage[i].argvIndex = 1; + pIdxInfo->aConstraintUsage[i].omit = 1; + } + + /* Terms of the form: langid = $langid */ + if( (iPlan & SPELLFIX_IDXNUM_LANGID)==0 + && pConstraint->iColumn==SPELLFIX_COL_LANGID + && pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ + ) { + iPlan |= SPELLFIX_IDXNUM_LANGID; + iLangTerm = i; + } + + /* Terms of the form: top = $top */ + if( (iPlan & SPELLFIX_IDXNUM_TOP)==0 + && pConstraint->iColumn==SPELLFIX_COL_TOP + && pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ + ) { + iPlan |= SPELLFIX_IDXNUM_TOP; + iTopTerm = i; + } + + /* Terms of the form: scope = $scope */ + if( (iPlan & SPELLFIX_IDXNUM_SCOPE)==0 + && pConstraint->iColumn==SPELLFIX_COL_SCOPE + && pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ + ) { + iPlan |= SPELLFIX_IDXNUM_SCOPE; + iScopeTerm = i; + } + + /* Terms of the form: distance < $dist or distance <= $dist */ + if( (iPlan & SPELLFIX_IDXNUM_DIST)==0 + && pConstraint->iColumn==SPELLFIX_COL_DISTANCE + && (pConstraint->op==SQLITE_INDEX_CONSTRAINT_LT + || pConstraint->op==SQLITE_INDEX_CONSTRAINT_LE) + ) { + if( pConstraint->op==SQLITE_INDEX_CONSTRAINT_LT ) { + iPlan |= SPELLFIX_IDXNUM_DISTLT; + } else { + iPlan |= SPELLFIX_IDXNUM_DISTLE; + } + iDistTerm = i; + } + + /* Terms of the form: distance < $dist or distance <= $dist */ + if( (iPlan & SPELLFIX_IDXNUM_ROWID)==0 + && pConstraint->iColumn<0 + && pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ + ) { + iPlan |= SPELLFIX_IDXNUM_ROWID; + iRowidTerm = i; + } + } + if( iPlan&SPELLFIX_IDXNUM_MATCH ) { + int idx = 2; + pIdxInfo->idxNum = iPlan; + if( pIdxInfo->nOrderBy==1 + && pIdxInfo->aOrderBy[0].iColumn==SPELLFIX_COL_SCORE + && pIdxInfo->aOrderBy[0].desc==0 + ) { + pIdxInfo->orderByConsumed = 1; /* Default order by iScore */ + } + if( iPlan&SPELLFIX_IDXNUM_LANGID ) { + pIdxInfo->aConstraintUsage[iLangTerm].argvIndex = idx++; + pIdxInfo->aConstraintUsage[iLangTerm].omit = 1; + } + if( iPlan&SPELLFIX_IDXNUM_TOP ) { + pIdxInfo->aConstraintUsage[iTopTerm].argvIndex = idx++; + pIdxInfo->aConstraintUsage[iTopTerm].omit = 1; + } + if( iPlan&SPELLFIX_IDXNUM_SCOPE ) { + pIdxInfo->aConstraintUsage[iScopeTerm].argvIndex = idx++; + pIdxInfo->aConstraintUsage[iScopeTerm].omit = 1; + } + if( iPlan&SPELLFIX_IDXNUM_DIST ) { + pIdxInfo->aConstraintUsage[iDistTerm].argvIndex = idx++; + pIdxInfo->aConstraintUsage[iDistTerm].omit = 1; + } + pIdxInfo->estimatedCost = 1e5; + } else if( (iPlan & SPELLFIX_IDXNUM_ROWID) ) { + pIdxInfo->idxNum = SPELLFIX_IDXNUM_ROWID; + pIdxInfo->aConstraintUsage[iRowidTerm].argvIndex = 1; + pIdxInfo->aConstraintUsage[iRowidTerm].omit = 1; + pIdxInfo->estimatedCost = 5; + } else { + pIdxInfo->idxNum = 0; + pIdxInfo->estimatedCost = 1e50; + } + return SQLITE_OK; } /* ** Open a new fuzzy-search cursor. */ -static int spellfix1Open(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){ - spellfix1_vtab *p = (spellfix1_vtab*)pVTab; - spellfix1_cursor *pCur; - pCur = sqlite3_malloc64( sizeof(*pCur) ); - if( pCur==0 ) return SQLITE_NOMEM; - memset(pCur, 0, sizeof(*pCur)); - pCur->pVTab = p; - *ppCursor = &pCur->base; - return SQLITE_OK; +static int spellfix1Open(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor) +{ + spellfix1_vtab *p = (spellfix1_vtab*)pVTab; + spellfix1_cursor *pCur; + pCur = sqlite3_malloc64( sizeof(*pCur) ); + if( pCur==0 ) return SQLITE_NOMEM; + memset(pCur, 0, sizeof(*pCur)); + pCur->pVTab = p; + *ppCursor = &pCur->base; + return SQLITE_OK; } /* ** Adjust a distance measurement by the words rank in order to show ** preference to common words. */ -static int spellfix1Score(int iDistance, int iRank){ - int iLog2; - for(iLog2=0; iRank>0; iLog2++, iRank>>=1){} - return iDistance + 32 - iLog2; +static int spellfix1Score(int iDistance, int iRank) +{ + int iLog2; + for(iLog2=0; iRank>0; iLog2++, iRank>>=1) {} + return iDistance + 32 - iLog2; } /* ** Compare two spellfix1_row objects for sorting purposes in qsort() such ** that they sort in order of increasing distance. */ -static int SQLITE_CDECL spellfix1RowCompare(const void *A, const void *B){ - const struct spellfix1_row *a = (const struct spellfix1_row*)A; - const struct spellfix1_row *b = (const struct spellfix1_row*)B; - return a->iScore - b->iScore; +static int SQLITE_CDECL spellfix1RowCompare(const void *A, const void *B) +{ + const struct spellfix1_row *a = (const struct spellfix1_row*)A; + const struct spellfix1_row *b = (const struct spellfix1_row*)B; + return a->iScore - b->iScore; } /* @@ -2364,159 +2435,160 @@ static int SQLITE_CDECL spellfix1RowCompare(const void *A, const void *B){ ** into spellfix1RunQuery(). */ typedef struct MatchQuery { - spellfix1_cursor *pCur; /* The cursor being queried */ - sqlite3_stmt *pStmt; /* shadow table query statment */ - char zHash[SPELLFIX_MX_HASH]; /* The current phonehash for zPattern */ - const char *zPattern; /* Transliterated input string */ - int nPattern; /* Length of zPattern */ - EditDist3FromString *pMatchStr3; /* Original unicode string */ - EditDist3Config *pConfig3; /* Edit-distance cost coefficients */ - const EditDist3Lang *pLang; /* The selected language coefficients */ - int iLang; /* The language id */ - int iScope; /* Default scope */ - int iMaxDist; /* Maximum allowed edit distance, or -1 */ - int rc; /* Error code */ - int nRun; /* Number of prior runs for the same zPattern */ - char azPrior[SPELLFIX_MX_RUN][SPELLFIX_MX_HASH]; /* Prior hashes */ + spellfix1_cursor *pCur; /* The cursor being queried */ + sqlite3_stmt *pStmt; /* shadow table query statment */ + char zHash[SPELLFIX_MX_HASH]; /* The current phonehash for zPattern */ + const char *zPattern; /* Transliterated input string */ + int nPattern; /* Length of zPattern */ + EditDist3FromString *pMatchStr3; /* Original unicode string */ + EditDist3Config *pConfig3; /* Edit-distance cost coefficients */ + const EditDist3Lang *pLang; /* The selected language coefficients */ + int iLang; /* The language id */ + int iScope; /* Default scope */ + int iMaxDist; /* Maximum allowed edit distance, or -1 */ + int rc; /* Error code */ + int nRun; /* Number of prior runs for the same zPattern */ + char azPrior[SPELLFIX_MX_RUN][SPELLFIX_MX_HASH]; /* Prior hashes */ } MatchQuery; /* ** Run a query looking for the best matches against zPattern using ** zHash as the character class seed hash. */ -static void spellfix1RunQuery(MatchQuery *p, const char *zQuery, int nQuery){ - const char *zK1; - const char *zWord; - int iDist; - int iRank; - int iScore; - int iWorst = 0; - int idx; - int idxWorst = -1; - int i; - int iScope = p->iScope; - spellfix1_cursor *pCur = p->pCur; - sqlite3_stmt *pStmt = p->pStmt; - char zHash1[SPELLFIX_MX_HASH]; - char zHash2[SPELLFIX_MX_HASH]; - char *zClass; - int nClass; - int rc; - - if( pCur->a==0 || p->rc ) return; /* Prior memory allocation failure */ - zClass = (char*)phoneticHash((unsigned char*)zQuery, nQuery); - if( zClass==0 ){ - p->rc = SQLITE_NOMEM; - return; - } - nClass = (int)strlen(zClass); - if( nClass>SPELLFIX_MX_HASH-2 ){ - nClass = SPELLFIX_MX_HASH-2; - zClass[nClass] = 0; - } - if( nClass<=iScope ){ - if( nClass>2 ){ - iScope = nClass-1; - }else{ - iScope = nClass; - } - } - memcpy(zHash1, zClass, iScope); - sqlite3_free(zClass); - zHash1[iScope] = 0; - memcpy(zHash2, zHash1, iScope); - zHash2[iScope] = 'Z'; - zHash2[iScope+1] = 0; +static void spellfix1RunQuery(MatchQuery *p, const char *zQuery, int nQuery) +{ + const char *zK1; + const char *zWord; + int iDist; + int iRank; + int iScore; + int iWorst = 0; + int idx; + int idxWorst = -1; + int i; + int iScope = p->iScope; + spellfix1_cursor *pCur = p->pCur; + sqlite3_stmt *pStmt = p->pStmt; + char zHash1[SPELLFIX_MX_HASH]; + char zHash2[SPELLFIX_MX_HASH]; + char *zClass; + int nClass; + int rc; + + if( pCur->a==0 || p->rc ) return; /* Prior memory allocation failure */ + zClass = (char*)phoneticHash((unsigned char*)zQuery, nQuery); + if( zClass==0 ) { + p->rc = SQLITE_NOMEM; + return; + } + nClass = (int)strlen(zClass); + if( nClass>SPELLFIX_MX_HASH-2 ) { + nClass = SPELLFIX_MX_HASH-2; + zClass[nClass] = 0; + } + if( nClass<=iScope ) { + if( nClass>2 ) { + iScope = nClass-1; + } else { + iScope = nClass; + } + } + memcpy(zHash1, zClass, iScope); + sqlite3_free(zClass); + zHash1[iScope] = 0; + memcpy(zHash2, zHash1, iScope); + zHash2[iScope] = 'Z'; + zHash2[iScope+1] = 0; #if SPELLFIX_MX_RUN>1 - for(i=0; inRun; i++){ - if( strcmp(p->azPrior[i], zHash1)==0 ) return; - } + for(i=0; inRun; i++) { + if( strcmp(p->azPrior[i], zHash1)==0 ) return; + } #endif - assert( p->nRunazPrior[p->nRun++], zHash1, iScope+1); - if( sqlite3_bind_text(pStmt, 1, zHash1, -1, SQLITE_STATIC)==SQLITE_NOMEM - || sqlite3_bind_text(pStmt, 2, zHash2, -1, SQLITE_STATIC)==SQLITE_NOMEM - ){ - p->rc = SQLITE_NOMEM; - return; - } + assert( p->nRunazPrior[p->nRun++], zHash1, iScope+1); + if( sqlite3_bind_text(pStmt, 1, zHash1, -1, SQLITE_STATIC)==SQLITE_NOMEM + || sqlite3_bind_text(pStmt, 2, zHash2, -1, SQLITE_STATIC)==SQLITE_NOMEM + ) { + p->rc = SQLITE_NOMEM; + return; + } #if SPELLFIX_MX_RUN>1 - for(i=0; inRow; i++){ - if( pCur->a[i].iScore>iWorst ){ - iWorst = pCur->a[i].iScore; - idxWorst = i; + for(i=0; inRow; i++) { + if( pCur->a[i].iScore>iWorst ) { + iWorst = pCur->a[i].iScore; + idxWorst = i; + } } - } #endif - while( sqlite3_step(pStmt)==SQLITE_ROW ){ - int iMatchlen = -1; - iRank = sqlite3_column_int(pStmt, 2); - if( p->pMatchStr3 ){ - int nWord = sqlite3_column_bytes(pStmt, 1); - zWord = (const char*)sqlite3_column_text(pStmt, 1); - iDist = editDist3Core(p->pMatchStr3, zWord, nWord, p->pLang, &iMatchlen); - }else{ - zK1 = (const char*)sqlite3_column_text(pStmt, 3); - if( zK1==0 ) continue; - iDist = editdist1(p->zPattern, zK1, 0); - } - if( iDist<0 ){ - p->rc = SQLITE_NOMEM; - break; - } - pCur->nSearch++; - - /* If there is a "distance < $dist" or "distance <= $dist" constraint, - ** check if this row meets it. If not, jump back up to the top of the - ** loop to process the next row. Otherwise, if the row does match the - ** distance constraint, check if the pCur->a[] array is already full. - ** If it is and no explicit "top = ?" constraint was present in the - ** query, grow the array to ensure there is room for the new entry. */ - assert( (p->iMaxDist>=0)==((pCur->idxNum & SPELLFIX_IDXNUM_DIST) ? 1 : 0) ); - if( p->iMaxDist>=0 ){ - if( iDist>p->iMaxDist ) continue; - if( pCur->nRow>=pCur->nAlloc && (pCur->idxNum & SPELLFIX_IDXNUM_TOP)==0 ){ - spellfix1ResizeCursor(pCur, pCur->nAlloc*2 + 10); - if( pCur->a==0 ) break; - } - } - - iScore = spellfix1Score(iDist,iRank); - if( pCur->nRownAlloc ){ - idx = pCur->nRow; - }else if( iScorea[idx].zWord); - }else{ - continue; - } - - pCur->a[idx].zWord = sqlite3_mprintf("%s", sqlite3_column_text(pStmt, 1)); - if( pCur->a[idx].zWord==0 ){ - p->rc = SQLITE_NOMEM; - break; - } - pCur->a[idx].iRowid = sqlite3_column_int64(pStmt, 0); - pCur->a[idx].iRank = iRank; - pCur->a[idx].iDistance = iDist; - pCur->a[idx].iScore = iScore; - pCur->a[idx].iMatchlen = iMatchlen; - memcpy(pCur->a[idx].zHash, zHash1, iScope+1); - if( pCur->nRownAlloc ) pCur->nRow++; - if( pCur->nRow==pCur->nAlloc ){ - iWorst = pCur->a[0].iScore; - idxWorst = 0; - for(i=1; inRow; i++){ - iScore = pCur->a[i].iScore; - if( iWorstrc = rc; + while( sqlite3_step(pStmt)==SQLITE_ROW ) { + int iMatchlen = -1; + iRank = sqlite3_column_int(pStmt, 2); + if( p->pMatchStr3 ) { + int nWord = sqlite3_column_bytes(pStmt, 1); + zWord = (const char*)sqlite3_column_text(pStmt, 1); + iDist = editDist3Core(p->pMatchStr3, zWord, nWord, p->pLang, &iMatchlen); + } else { + zK1 = (const char*)sqlite3_column_text(pStmt, 3); + if( zK1==0 ) continue; + iDist = editdist1(p->zPattern, zK1, 0); + } + if( iDist<0 ) { + p->rc = SQLITE_NOMEM; + break; + } + pCur->nSearch++; + + /* If there is a "distance < $dist" or "distance <= $dist" constraint, + ** check if this row meets it. If not, jump back up to the top of the + ** loop to process the next row. Otherwise, if the row does match the + ** distance constraint, check if the pCur->a[] array is already full. + ** If it is and no explicit "top = ?" constraint was present in the + ** query, grow the array to ensure there is room for the new entry. */ + assert( (p->iMaxDist>=0)==((pCur->idxNum & SPELLFIX_IDXNUM_DIST) ? 1 : 0) ); + if( p->iMaxDist>=0 ) { + if( iDist>p->iMaxDist ) continue; + if( pCur->nRow>=pCur->nAlloc && (pCur->idxNum & SPELLFIX_IDXNUM_TOP)==0 ) { + spellfix1ResizeCursor(pCur, pCur->nAlloc*2 + 10); + if( pCur->a==0 ) break; + } + } + + iScore = spellfix1Score(iDist,iRank); + if( pCur->nRownAlloc ) { + idx = pCur->nRow; + } else if( iScorea[idx].zWord); + } else { + continue; + } + + pCur->a[idx].zWord = sqlite3_mprintf("%s", sqlite3_column_text(pStmt, 1)); + if( pCur->a[idx].zWord==0 ) { + p->rc = SQLITE_NOMEM; + break; + } + pCur->a[idx].iRowid = sqlite3_column_int64(pStmt, 0); + pCur->a[idx].iRank = iRank; + pCur->a[idx].iDistance = iDist; + pCur->a[idx].iScore = iScore; + pCur->a[idx].iMatchlen = iMatchlen; + memcpy(pCur->a[idx].zHash, zHash1, iScope+1); + if( pCur->nRownAlloc ) pCur->nRow++; + if( pCur->nRow==pCur->nAlloc ) { + iWorst = pCur->a[0].iScore; + idxWorst = 0; + for(i=1; inRow; i++) { + iScore = pCur->a[i].iScore; + if( iWorstrc = rc; } /* @@ -2524,300 +2596,310 @@ static void spellfix1RunQuery(MatchQuery *p, const char *zQuery, int nQuery){ ** and we are doing a scan. */ static int spellfix1FilterForMatch( - spellfix1_cursor *pCur, - int argc, - sqlite3_value **argv -){ - int idxNum = pCur->idxNum; - const unsigned char *zMatchThis; /* RHS of the MATCH operator */ - EditDist3FromString *pMatchStr3 = 0; /* zMatchThis as an editdist string */ - char *zPattern; /* Transliteration of zMatchThis */ - int nPattern; /* Length of zPattern */ - int iLimit = 20; /* Max number of rows of output */ - int iScope = 3; /* Use this many characters of zClass */ - int iLang = 0; /* Language code */ - char *zSql; /* SQL of shadow table query */ - sqlite3_stmt *pStmt = 0; /* Shadow table query */ - int rc; /* Result code */ - int idx = 1; /* Next available filter parameter */ - spellfix1_vtab *p = pCur->pVTab; /* The virtual table that owns pCur */ - MatchQuery x; /* For passing info to RunQuery() */ - - /* Load the cost table if we have not already done so */ - if( p->zCostTable!=0 && p->pConfig3==0 ){ - p->pConfig3 = sqlite3_malloc64( sizeof(p->pConfig3[0]) ); - if( p->pConfig3==0 ) return SQLITE_NOMEM; - memset(p->pConfig3, 0, sizeof(p->pConfig3[0])); - rc = editDist3ConfigLoad(p->pConfig3, p->db, p->zCostTable); - if( rc ) return rc; - } - memset(&x, 0, sizeof(x)); - x.iScope = 3; /* Default scope if none specified by "WHERE scope=N" */ - x.iMaxDist = -1; /* Maximum allowed edit distance */ - - if( idxNum&2 ){ - iLang = sqlite3_value_int(argv[idx++]); - } - if( idxNum&4 ){ - iLimit = sqlite3_value_int(argv[idx++]); - if( iLimit<1 ) iLimit = 1; - } - if( idxNum&8 ){ - x.iScope = sqlite3_value_int(argv[idx++]); - if( x.iScope<1 ) x.iScope = 1; - if( x.iScope>SPELLFIX_MX_HASH-2 ) x.iScope = SPELLFIX_MX_HASH-2; - } - if( idxNum&(16|32) ){ - x.iMaxDist = sqlite3_value_int(argv[idx++]); - if( idxNum&16 ) x.iMaxDist--; - if( x.iMaxDist<0 ) x.iMaxDist = 0; - } - spellfix1ResetCursor(pCur); - spellfix1ResizeCursor(pCur, iLimit); - zMatchThis = sqlite3_value_text(argv[0]); - if( zMatchThis==0 ) return SQLITE_OK; - if( p->pConfig3 ){ - x.pLang = editDist3FindLang(p->pConfig3, iLang); - pMatchStr3 = editDist3FromStringNew(x.pLang, (const char*)zMatchThis, -1); - if( pMatchStr3==0 ){ - x.rc = SQLITE_NOMEM; - goto filter_exit; - } - }else{ - x.pLang = 0; - } - zPattern = (char*)transliterate(zMatchThis, sqlite3_value_bytes(argv[0])); - sqlite3_free(pCur->zPattern); - pCur->zPattern = zPattern; - if( zPattern==0 ){ - x.rc = SQLITE_NOMEM; - goto filter_exit; - } - nPattern = (int)strlen(zPattern); - if( zPattern[nPattern-1]=='*' ) nPattern--; - zSql = sqlite3_mprintf( - "SELECT id, word, rank, coalesce(k1,word)" - " FROM \"%w\".\"%w_vocab\"" - " WHERE langid=%d AND k2>=?1 AND k2zDbName, p->zTableName, iLang - ); - if( zSql==0 ){ - x.rc = SQLITE_NOMEM; - pStmt = 0; - goto filter_exit; - } - rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); - sqlite3_free(zSql); - pCur->iLang = iLang; - x.pCur = pCur; - x.pStmt = pStmt; - x.zPattern = zPattern; - x.nPattern = nPattern; - x.pMatchStr3 = pMatchStr3; - x.iLang = iLang; - x.rc = rc; - x.pConfig3 = p->pConfig3; - if( x.rc==SQLITE_OK ){ - spellfix1RunQuery(&x, zPattern, nPattern); - } - - if( pCur->a ){ - qsort(pCur->a, pCur->nRow, sizeof(pCur->a[0]), spellfix1RowCompare); - pCur->iTop = iLimit; - pCur->iScope = iScope; - }else{ - x.rc = SQLITE_NOMEM; - } + spellfix1_cursor *pCur, + int argc, + sqlite3_value **argv +) +{ + int idxNum = pCur->idxNum; + const unsigned char *zMatchThis; /* RHS of the MATCH operator */ + EditDist3FromString *pMatchStr3 = 0; /* zMatchThis as an editdist string */ + char *zPattern; /* Transliteration of zMatchThis */ + int nPattern; /* Length of zPattern */ + int iLimit = 20; /* Max number of rows of output */ + int iScope = 3; /* Use this many characters of zClass */ + int iLang = 0; /* Language code */ + char *zSql; /* SQL of shadow table query */ + sqlite3_stmt *pStmt = 0; /* Shadow table query */ + int rc; /* Result code */ + int idx = 1; /* Next available filter parameter */ + spellfix1_vtab *p = pCur->pVTab; /* The virtual table that owns pCur */ + MatchQuery x; /* For passing info to RunQuery() */ + + /* Load the cost table if we have not already done so */ + if( p->zCostTable!=0 && p->pConfig3==0 ) { + p->pConfig3 = sqlite3_malloc64( sizeof(p->pConfig3[0]) ); + if( p->pConfig3==0 ) return SQLITE_NOMEM; + memset(p->pConfig3, 0, sizeof(p->pConfig3[0])); + rc = editDist3ConfigLoad(p->pConfig3, p->db, p->zCostTable); + if( rc ) return rc; + } + memset(&x, 0, sizeof(x)); + x.iScope = 3; /* Default scope if none specified by "WHERE scope=N" */ + x.iMaxDist = -1; /* Maximum allowed edit distance */ + + if( idxNum&2 ) { + iLang = sqlite3_value_int(argv[idx++]); + } + if( idxNum&4 ) { + iLimit = sqlite3_value_int(argv[idx++]); + if( iLimit<1 ) iLimit = 1; + } + if( idxNum&8 ) { + x.iScope = sqlite3_value_int(argv[idx++]); + if( x.iScope<1 ) x.iScope = 1; + if( x.iScope>SPELLFIX_MX_HASH-2 ) x.iScope = SPELLFIX_MX_HASH-2; + } + if( idxNum&(16|32) ) { + x.iMaxDist = sqlite3_value_int(argv[idx++]); + if( idxNum&16 ) x.iMaxDist--; + if( x.iMaxDist<0 ) x.iMaxDist = 0; + } + spellfix1ResetCursor(pCur); + spellfix1ResizeCursor(pCur, iLimit); + zMatchThis = sqlite3_value_text(argv[0]); + if( zMatchThis==0 ) return SQLITE_OK; + if( p->pConfig3 ) { + x.pLang = editDist3FindLang(p->pConfig3, iLang); + pMatchStr3 = editDist3FromStringNew(x.pLang, (const char*)zMatchThis, -1); + if( pMatchStr3==0 ) { + x.rc = SQLITE_NOMEM; + goto filter_exit; + } + } else { + x.pLang = 0; + } + zPattern = (char*)transliterate(zMatchThis, sqlite3_value_bytes(argv[0])); + sqlite3_free(pCur->zPattern); + pCur->zPattern = zPattern; + if( zPattern==0 ) { + x.rc = SQLITE_NOMEM; + goto filter_exit; + } + nPattern = (int)strlen(zPattern); + if( zPattern[nPattern-1]=='*' ) nPattern--; + zSql = sqlite3_mprintf( + "SELECT id, word, rank, coalesce(k1,word)" + " FROM \"%w\".\"%w_vocab\"" + " WHERE langid=%d AND k2>=?1 AND k2zDbName, p->zTableName, iLang + ); + if( zSql==0 ) { + x.rc = SQLITE_NOMEM; + pStmt = 0; + goto filter_exit; + } + rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0); + sqlite3_free(zSql); + pCur->iLang = iLang; + x.pCur = pCur; + x.pStmt = pStmt; + x.zPattern = zPattern; + x.nPattern = nPattern; + x.pMatchStr3 = pMatchStr3; + x.iLang = iLang; + x.rc = rc; + x.pConfig3 = p->pConfig3; + if( x.rc==SQLITE_OK ) { + spellfix1RunQuery(&x, zPattern, nPattern); + } + + if( pCur->a ) { + qsort(pCur->a, pCur->nRow, sizeof(pCur->a[0]), spellfix1RowCompare); + pCur->iTop = iLimit; + pCur->iScope = iScope; + } else { + x.rc = SQLITE_NOMEM; + } filter_exit: - sqlite3_finalize(pStmt); - editDist3FromStringDelete(pMatchStr3); - return x.rc; + sqlite3_finalize(pStmt); + editDist3FromStringDelete(pMatchStr3); + return x.rc; } /* ** This version of xFilter handles a full-table scan case */ static int spellfix1FilterForFullScan( - spellfix1_cursor *pCur, - int argc, - sqlite3_value **argv -){ - int rc = SQLITE_OK; - int idxNum = pCur->idxNum; - char *zSql; - spellfix1_vtab *pVTab = pCur->pVTab; - spellfix1ResetCursor(pCur); - assert( idxNum==0 || idxNum==64 ); - zSql = sqlite3_mprintf( - "SELECT word, rank, NULL, langid, id FROM \"%w\".\"%w_vocab\"%s", - pVTab->zDbName, pVTab->zTableName, - ((idxNum & 64) ? " WHERE rowid=?" : "") - ); - if( zSql==0 ) return SQLITE_NOMEM; - rc = sqlite3_prepare_v2(pVTab->db, zSql, -1, &pCur->pFullScan, 0); - sqlite3_free(zSql); - if( rc==SQLITE_OK && (idxNum & 64) ){ - assert( argc==1 ); - rc = sqlite3_bind_value(pCur->pFullScan, 1, argv[0]); - } - pCur->nRow = pCur->iRow = 0; - if( rc==SQLITE_OK ){ - rc = sqlite3_step(pCur->pFullScan); - if( rc==SQLITE_ROW ){ pCur->iRow = -1; rc = SQLITE_OK; } - if( rc==SQLITE_DONE ){ rc = SQLITE_OK; } - }else{ - pCur->iRow = 0; - } - return rc; + spellfix1_cursor *pCur, + int argc, + sqlite3_value **argv +) +{ + int rc = SQLITE_OK; + int idxNum = pCur->idxNum; + char *zSql; + spellfix1_vtab *pVTab = pCur->pVTab; + spellfix1ResetCursor(pCur); + assert( idxNum==0 || idxNum==64 ); + zSql = sqlite3_mprintf( + "SELECT word, rank, NULL, langid, id FROM \"%w\".\"%w_vocab\"%s", + pVTab->zDbName, pVTab->zTableName, + ((idxNum & 64) ? " WHERE rowid=?" : "") + ); + if( zSql==0 ) return SQLITE_NOMEM; + rc = sqlite3_prepare_v2(pVTab->db, zSql, -1, &pCur->pFullScan, 0); + sqlite3_free(zSql); + if( rc==SQLITE_OK && (idxNum & 64) ) { + assert( argc==1 ); + rc = sqlite3_bind_value(pCur->pFullScan, 1, argv[0]); + } + pCur->nRow = pCur->iRow = 0; + if( rc==SQLITE_OK ) { + rc = sqlite3_step(pCur->pFullScan); + if( rc==SQLITE_ROW ) { + pCur->iRow = -1; + rc = SQLITE_OK; + } + if( rc==SQLITE_DONE ) { + rc = SQLITE_OK; + } + } else { + pCur->iRow = 0; + } + return rc; } - /* ** Called to "rewind" a cursor back to the beginning so that ** it starts its output over again. Always called at least once ** prior to any spellfix1Column, spellfix1Rowid, or spellfix1Eof call. */ static int spellfix1Filter( - sqlite3_vtab_cursor *cur, - int idxNum, const char *idxStr, - int argc, sqlite3_value **argv -){ - spellfix1_cursor *pCur = (spellfix1_cursor *)cur; - int rc; - pCur->idxNum = idxNum; - if( idxNum & 1 ){ - rc = spellfix1FilterForMatch(pCur, argc, argv); - }else{ - rc = spellfix1FilterForFullScan(pCur, argc, argv); - } - return rc; + sqlite3_vtab_cursor *cur, + int idxNum, const char *idxStr, + int argc, sqlite3_value **argv +) +{ + spellfix1_cursor *pCur = (spellfix1_cursor *)cur; + int rc; + pCur->idxNum = idxNum; + if( idxNum & 1 ) { + rc = spellfix1FilterForMatch(pCur, argc, argv); + } else { + rc = spellfix1FilterForFullScan(pCur, argc, argv); + } + return rc; } - /* ** Advance a cursor to its next row of output */ -static int spellfix1Next(sqlite3_vtab_cursor *cur){ - spellfix1_cursor *pCur = (spellfix1_cursor *)cur; - int rc = SQLITE_OK; - if( pCur->iRow < pCur->nRow ){ - if( pCur->pFullScan ){ - rc = sqlite3_step(pCur->pFullScan); - if( rc!=SQLITE_ROW ) pCur->iRow = pCur->nRow; - if( rc==SQLITE_ROW || rc==SQLITE_DONE ) rc = SQLITE_OK; - }else{ - pCur->iRow++; - } - } - return rc; +static int spellfix1Next(sqlite3_vtab_cursor *cur) +{ + spellfix1_cursor *pCur = (spellfix1_cursor *)cur; + int rc = SQLITE_OK; + if( pCur->iRow < pCur->nRow ) { + if( pCur->pFullScan ) { + rc = sqlite3_step(pCur->pFullScan); + if( rc!=SQLITE_ROW ) pCur->iRow = pCur->nRow; + if( rc==SQLITE_ROW || rc==SQLITE_DONE ) rc = SQLITE_OK; + } else { + pCur->iRow++; + } + } + return rc; } /* ** Return TRUE if we are at the end-of-file */ -static int spellfix1Eof(sqlite3_vtab_cursor *cur){ - spellfix1_cursor *pCur = (spellfix1_cursor *)cur; - return pCur->iRow>=pCur->nRow; +static int spellfix1Eof(sqlite3_vtab_cursor *cur) +{ + spellfix1_cursor *pCur = (spellfix1_cursor *)cur; + return pCur->iRow>=pCur->nRow; } /* ** Return columns from the current row. */ static int spellfix1Column( - sqlite3_vtab_cursor *cur, - sqlite3_context *ctx, - int i -){ - spellfix1_cursor *pCur = (spellfix1_cursor*)cur; - if( pCur->pFullScan ){ - if( i<=SPELLFIX_COL_LANGID ){ - sqlite3_result_value(ctx, sqlite3_column_value(pCur->pFullScan, i)); - }else{ - sqlite3_result_null(ctx); + sqlite3_vtab_cursor *cur, + sqlite3_context *ctx, + int i +) +{ + spellfix1_cursor *pCur = (spellfix1_cursor*)cur; + if( pCur->pFullScan ) { + if( i<=SPELLFIX_COL_LANGID ) { + sqlite3_result_value(ctx, sqlite3_column_value(pCur->pFullScan, i)); + } else { + sqlite3_result_null(ctx); + } + return SQLITE_OK; } - return SQLITE_OK; - } - switch( i ){ + switch( i ) { case SPELLFIX_COL_WORD: { - sqlite3_result_text(ctx, pCur->a[pCur->iRow].zWord, -1, SQLITE_STATIC); - break; + sqlite3_result_text(ctx, pCur->a[pCur->iRow].zWord, -1, SQLITE_STATIC); + break; } case SPELLFIX_COL_RANK: { - sqlite3_result_int(ctx, pCur->a[pCur->iRow].iRank); - break; + sqlite3_result_int(ctx, pCur->a[pCur->iRow].iRank); + break; } case SPELLFIX_COL_DISTANCE: { - sqlite3_result_int(ctx, pCur->a[pCur->iRow].iDistance); - break; + sqlite3_result_int(ctx, pCur->a[pCur->iRow].iDistance); + break; } case SPELLFIX_COL_LANGID: { - sqlite3_result_int(ctx, pCur->iLang); - break; + sqlite3_result_int(ctx, pCur->iLang); + break; } case SPELLFIX_COL_SCORE: { - sqlite3_result_int(ctx, pCur->a[pCur->iRow].iScore); - break; + sqlite3_result_int(ctx, pCur->a[pCur->iRow].iScore); + break; } case SPELLFIX_COL_MATCHLEN: { - int iMatchlen = pCur->a[pCur->iRow].iMatchlen; - if( iMatchlen<0 ){ - int nPattern = (int)strlen(pCur->zPattern); - char *zWord = pCur->a[pCur->iRow].zWord; - int nWord = (int)strlen(zWord); - - if( nPattern>0 && pCur->zPattern[nPattern-1]=='*' ){ - char *zTranslit; - int res; - zTranslit = (char *)transliterate((unsigned char *)zWord, nWord); - if( !zTranslit ) return SQLITE_NOMEM; - res = editdist1(pCur->zPattern, zTranslit, &iMatchlen); - sqlite3_free(zTranslit); - if( res<0 ) return SQLITE_NOMEM; - iMatchlen = translen_to_charlen(zWord, nWord, iMatchlen); - }else{ - iMatchlen = utf8Charlen(zWord, nWord); - } - } - - sqlite3_result_int(ctx, iMatchlen); - break; + int iMatchlen = pCur->a[pCur->iRow].iMatchlen; + if( iMatchlen<0 ) { + int nPattern = (int)strlen(pCur->zPattern); + char *zWord = pCur->a[pCur->iRow].zWord; + int nWord = (int)strlen(zWord); + + if( nPattern>0 && pCur->zPattern[nPattern-1]=='*' ) { + char *zTranslit; + int res; + zTranslit = (char *)transliterate((unsigned char *)zWord, nWord); + if( !zTranslit ) return SQLITE_NOMEM; + res = editdist1(pCur->zPattern, zTranslit, &iMatchlen); + sqlite3_free(zTranslit); + if( res<0 ) return SQLITE_NOMEM; + iMatchlen = translen_to_charlen(zWord, nWord, iMatchlen); + } else { + iMatchlen = utf8Charlen(zWord, nWord); + } + } + + sqlite3_result_int(ctx, iMatchlen); + break; } case SPELLFIX_COL_PHONEHASH: { - sqlite3_result_text(ctx, pCur->a[pCur->iRow].zHash, -1, SQLITE_STATIC); - break; + sqlite3_result_text(ctx, pCur->a[pCur->iRow].zHash, -1, SQLITE_STATIC); + break; } case SPELLFIX_COL_TOP: { - sqlite3_result_int(ctx, pCur->iTop); - break; + sqlite3_result_int(ctx, pCur->iTop); + break; } case SPELLFIX_COL_SCOPE: { - sqlite3_result_int(ctx, pCur->iScope); - break; + sqlite3_result_int(ctx, pCur->iScope); + break; } case SPELLFIX_COL_SRCHCNT: { - sqlite3_result_int(ctx, pCur->nSearch); - break; + sqlite3_result_int(ctx, pCur->nSearch); + break; } default: { - sqlite3_result_null(ctx); - break; + sqlite3_result_null(ctx); + break; } - } - return SQLITE_OK; + } + return SQLITE_OK; } /* ** The rowid. */ -static int spellfix1Rowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ - spellfix1_cursor *pCur = (spellfix1_cursor*)cur; - if( pCur->pFullScan ){ - *pRowid = sqlite3_column_int64(pCur->pFullScan, 4); - }else{ - *pRowid = pCur->a[pCur->iRow].iRowid; - } - return SQLITE_OK; +static int spellfix1Rowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid) +{ + spellfix1_cursor *pCur = (spellfix1_cursor*)cur; + if( pCur->pFullScan ) { + *pRowid = sqlite3_column_int64(pCur->pFullScan, 4); + } else { + *pRowid = pCur->a[pCur->iRow].iRowid; + } + return SQLITE_OK; } /* @@ -2825,234 +2907,237 @@ static int spellfix1Rowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){ ** containing the conflict mode that xUpdate() should use for the current ** operation. One of: "ROLLBACK", "IGNORE", "ABORT" or "REPLACE". */ -static const char *spellfix1GetConflict(sqlite3 *db){ - static const char *azConflict[] = { - /* Note: Instead of "FAIL" - "ABORT". */ - "ROLLBACK", "IGNORE", "ABORT", "ABORT", "REPLACE" - }; - int eConflict = sqlite3_vtab_on_conflict(db); - - assert( eConflict==SQLITE_ROLLBACK || eConflict==SQLITE_IGNORE - || eConflict==SQLITE_FAIL || eConflict==SQLITE_ABORT - || eConflict==SQLITE_REPLACE - ); - assert( SQLITE_ROLLBACK==1 ); - assert( SQLITE_IGNORE==2 ); - assert( SQLITE_FAIL==3 ); - assert( SQLITE_ABORT==4 ); - assert( SQLITE_REPLACE==5 ); - - return azConflict[eConflict-1]; +static const char *spellfix1GetConflict(sqlite3 *db) +{ + static const char *azConflict[] = { + /* Note: Instead of "FAIL" - "ABORT". */ + "ROLLBACK", "IGNORE", "ABORT", "ABORT", "REPLACE" + }; + int eConflict = sqlite3_vtab_on_conflict(db); + + assert( eConflict==SQLITE_ROLLBACK || eConflict==SQLITE_IGNORE + || eConflict==SQLITE_FAIL || eConflict==SQLITE_ABORT + || eConflict==SQLITE_REPLACE + ); + assert( SQLITE_ROLLBACK==1 ); + assert( SQLITE_IGNORE==2 ); + assert( SQLITE_FAIL==3 ); + assert( SQLITE_ABORT==4 ); + assert( SQLITE_REPLACE==5 ); + + return azConflict[eConflict-1]; } /* ** The xUpdate() method. */ static int spellfix1Update( - sqlite3_vtab *pVTab, - int argc, - sqlite3_value **argv, - sqlite_int64 *pRowid -){ - int rc = SQLITE_OK; - sqlite3_int64 rowid, newRowid; - spellfix1_vtab *p = (spellfix1_vtab*)pVTab; - sqlite3 *db = p->db; - - if( argc==1 ){ - /* A delete operation on the rowid given by argv[0] */ - rowid = *pRowid = sqlite3_value_int64(argv[0]); - spellfix1DbExec(&rc, db, "DELETE FROM \"%w\".\"%w_vocab\" " - " WHERE id=%lld", - p->zDbName, p->zTableName, rowid); - }else{ - const unsigned char *zWord = sqlite3_value_text(argv[SPELLFIX_COL_WORD+2]); - int nWord = sqlite3_value_bytes(argv[SPELLFIX_COL_WORD+2]); - int iLang = sqlite3_value_int(argv[SPELLFIX_COL_LANGID+2]); - int iRank = sqlite3_value_int(argv[SPELLFIX_COL_RANK+2]); - const unsigned char *zSoundslike = - sqlite3_value_text(argv[SPELLFIX_COL_SOUNDSLIKE+2]); - int nSoundslike = sqlite3_value_bytes(argv[SPELLFIX_COL_SOUNDSLIKE+2]); - char *zK1, *zK2; - int i; - char c; - const char *zConflict = spellfix1GetConflict(db); - - if( zWord==0 ){ - /* Inserts of the form: INSERT INTO table(command) VALUES('xyzzy'); - ** cause zWord to be NULL, so we look at the "command" column to see - ** what special actions to take */ - const char *zCmd = - (const char*)sqlite3_value_text(argv[SPELLFIX_COL_COMMAND+2]); - if( zCmd==0 ){ - pVTab->zErrMsg = sqlite3_mprintf("NOT NULL constraint failed: %s.word", - p->zTableName); - return SQLITE_CONSTRAINT_NOTNULL; - } - if( strcmp(zCmd,"reset")==0 ){ - /* Reset the edit cost table (if there is one). */ - editDist3ConfigDelete(p->pConfig3); - p->pConfig3 = 0; - return SQLITE_OK; - } - if( strncmp(zCmd,"edit_cost_table=",16)==0 ){ - editDist3ConfigDelete(p->pConfig3); - p->pConfig3 = 0; - sqlite3_free(p->zCostTable); - p->zCostTable = spellfix1Dequote(zCmd+16); - if( p->zCostTable==0 ) return SQLITE_NOMEM; - if( p->zCostTable[0]==0 || sqlite3_stricmp(p->zCostTable,"null")==0 ){ - sqlite3_free(p->zCostTable); - p->zCostTable = 0; + sqlite3_vtab *pVTab, + int argc, + sqlite3_value **argv, + sqlite_int64 *pRowid +) +{ + int rc = SQLITE_OK; + sqlite3_int64 rowid, newRowid; + spellfix1_vtab *p = (spellfix1_vtab*)pVTab; + sqlite3 *db = p->db; + + if( argc==1 ) { + /* A delete operation on the rowid given by argv[0] */ + rowid = *pRowid = sqlite3_value_int64(argv[0]); + spellfix1DbExec(&rc, db, "DELETE FROM \"%w\".\"%w_vocab\" " + " WHERE id=%lld", + p->zDbName, p->zTableName, rowid); + } else { + const unsigned char *zWord = sqlite3_value_text(argv[SPELLFIX_COL_WORD+2]); + int nWord = sqlite3_value_bytes(argv[SPELLFIX_COL_WORD+2]); + int iLang = sqlite3_value_int(argv[SPELLFIX_COL_LANGID+2]); + int iRank = sqlite3_value_int(argv[SPELLFIX_COL_RANK+2]); + const unsigned char *zSoundslike = + sqlite3_value_text(argv[SPELLFIX_COL_SOUNDSLIKE+2]); + int nSoundslike = sqlite3_value_bytes(argv[SPELLFIX_COL_SOUNDSLIKE+2]); + char *zK1, *zK2; + int i; + char c; + const char *zConflict = spellfix1GetConflict(db); + + if( zWord==0 ) { + /* Inserts of the form: INSERT INTO table(command) VALUES('xyzzy'); + ** cause zWord to be NULL, so we look at the "command" column to see + ** what special actions to take */ + const char *zCmd = + (const char*)sqlite3_value_text(argv[SPELLFIX_COL_COMMAND+2]); + if( zCmd==0 ) { + pVTab->zErrMsg = sqlite3_mprintf("NOT NULL constraint failed: %s.word", + p->zTableName); + return SQLITE_CONSTRAINT_NOTNULL; + } + if( strcmp(zCmd,"reset")==0 ) { + /* Reset the edit cost table (if there is one). */ + editDist3ConfigDelete(p->pConfig3); + p->pConfig3 = 0; + return SQLITE_OK; + } + if( strncmp(zCmd,"edit_cost_table=",16)==0 ) { + editDist3ConfigDelete(p->pConfig3); + p->pConfig3 = 0; + sqlite3_free(p->zCostTable); + p->zCostTable = spellfix1Dequote(zCmd+16); + if( p->zCostTable==0 ) return SQLITE_NOMEM; + if( p->zCostTable[0]==0 || sqlite3_stricmp(p->zCostTable,"null")==0 ) { + sqlite3_free(p->zCostTable); + p->zCostTable = 0; + } + return SQLITE_OK; + } + pVTab->zErrMsg = sqlite3_mprintf("unknown value for %s.command: \"%w\"", + p->zTableName, zCmd); + return SQLITE_ERROR; } - return SQLITE_OK; - } - pVTab->zErrMsg = sqlite3_mprintf("unknown value for %s.command: \"%w\"", - p->zTableName, zCmd); - return SQLITE_ERROR; - } - if( iRank<1 ) iRank = 1; - if( zSoundslike ){ - zK1 = (char*)transliterate(zSoundslike, nSoundslike); - }else{ - zK1 = (char*)transliterate(zWord, nWord); - } - if( zK1==0 ) return SQLITE_NOMEM; - for(i=0; (c = zK1[i])!=0; i++){ - if( c>='A' && c<='Z' ) zK1[i] += 'a' - 'A'; - } - zK2 = (char*)phoneticHash((const unsigned char*)zK1, i); - if( zK2==0 ){ - sqlite3_free(zK1); - return SQLITE_NOMEM; - } - if( sqlite3_value_type(argv[0])==SQLITE_NULL ){ - if( sqlite3_value_type(argv[1])==SQLITE_NULL ){ - spellfix1DbExec(&rc, db, - "INSERT INTO \"%w\".\"%w_vocab\"(rank,langid,word,k1,k2) " - "VALUES(%d,%d,%Q,nullif(%Q,%Q),%Q)", - p->zDbName, p->zTableName, - iRank, iLang, zWord, zK1, zWord, zK2 - ); - }else{ - newRowid = sqlite3_value_int64(argv[1]); - spellfix1DbExec(&rc, db, - "INSERT OR %s INTO \"%w\".\"%w_vocab\"(id,rank,langid,word,k1,k2) " - "VALUES(%lld,%d,%d,%Q,nullif(%Q,%Q),%Q)", - zConflict, p->zDbName, p->zTableName, - newRowid, iRank, iLang, zWord, zK1, zWord, zK2 - ); - } - *pRowid = sqlite3_last_insert_rowid(db); - }else{ - rowid = sqlite3_value_int64(argv[0]); - newRowid = *pRowid = sqlite3_value_int64(argv[1]); - spellfix1DbExec(&rc, db, - "UPDATE OR %s \"%w\".\"%w_vocab\" SET id=%lld, rank=%d, langid=%d," - " word=%Q, k1=nullif(%Q,%Q), k2=%Q WHERE id=%lld", - zConflict, p->zDbName, p->zTableName, newRowid, iRank, iLang, - zWord, zK1, zWord, zK2, rowid - ); - } - sqlite3_free(zK1); - sqlite3_free(zK2); - } - return rc; + if( iRank<1 ) iRank = 1; + if( zSoundslike ) { + zK1 = (char*)transliterate(zSoundslike, nSoundslike); + } else { + zK1 = (char*)transliterate(zWord, nWord); + } + if( zK1==0 ) return SQLITE_NOMEM; + for(i=0; (c = zK1[i])!=0; i++) { + if( c>='A' && c<='Z' ) zK1[i] += 'a' - 'A'; + } + zK2 = (char*)phoneticHash((const unsigned char*)zK1, i); + if( zK2==0 ) { + sqlite3_free(zK1); + return SQLITE_NOMEM; + } + if( sqlite3_value_type(argv[0])==SQLITE_NULL ) { + if( sqlite3_value_type(argv[1])==SQLITE_NULL ) { + spellfix1DbExec(&rc, db, + "INSERT INTO \"%w\".\"%w_vocab\"(rank,langid,word,k1,k2) " + "VALUES(%d,%d,%Q,nullif(%Q,%Q),%Q)", + p->zDbName, p->zTableName, + iRank, iLang, zWord, zK1, zWord, zK2 + ); + } else { + newRowid = sqlite3_value_int64(argv[1]); + spellfix1DbExec(&rc, db, + "INSERT OR %s INTO \"%w\".\"%w_vocab\"(id,rank,langid,word,k1,k2) " + "VALUES(%lld,%d,%d,%Q,nullif(%Q,%Q),%Q)", + zConflict, p->zDbName, p->zTableName, + newRowid, iRank, iLang, zWord, zK1, zWord, zK2 + ); + } + *pRowid = sqlite3_last_insert_rowid(db); + } else { + rowid = sqlite3_value_int64(argv[0]); + newRowid = *pRowid = sqlite3_value_int64(argv[1]); + spellfix1DbExec(&rc, db, + "UPDATE OR %s \"%w\".\"%w_vocab\" SET id=%lld, rank=%d, langid=%d," + " word=%Q, k1=nullif(%Q,%Q), k2=%Q WHERE id=%lld", + zConflict, p->zDbName, p->zTableName, newRowid, iRank, iLang, + zWord, zK1, zWord, zK2, rowid + ); + } + sqlite3_free(zK1); + sqlite3_free(zK2); + } + return rc; } /* ** Rename the spellfix1 table. */ -static int spellfix1Rename(sqlite3_vtab *pVTab, const char *zNew){ - spellfix1_vtab *p = (spellfix1_vtab*)pVTab; - sqlite3 *db = p->db; - int rc = SQLITE_OK; - char *zNewName = sqlite3_mprintf("%s", zNew); - if( zNewName==0 ){ - return SQLITE_NOMEM; - } - spellfix1DbExec(&rc, db, - "ALTER TABLE \"%w\".\"%w_vocab\" RENAME TO \"%w_vocab\"", - p->zDbName, p->zTableName, zNewName - ); - if( rc==SQLITE_OK ){ - sqlite3_free(p->zTableName); - p->zTableName = zNewName; - }else{ - sqlite3_free(zNewName); - } - return rc; +static int spellfix1Rename(sqlite3_vtab *pVTab, const char *zNew) +{ + spellfix1_vtab *p = (spellfix1_vtab*)pVTab; + sqlite3 *db = p->db; + int rc = SQLITE_OK; + char *zNewName = sqlite3_mprintf("%s", zNew); + if( zNewName==0 ) { + return SQLITE_NOMEM; + } + spellfix1DbExec(&rc, db, + "ALTER TABLE \"%w\".\"%w_vocab\" RENAME TO \"%w_vocab\"", + p->zDbName, p->zTableName, zNewName + ); + if( rc==SQLITE_OK ) { + sqlite3_free(p->zTableName); + p->zTableName = zNewName; + } else { + sqlite3_free(zNewName); + } + return rc; } - /* ** A virtual table module that provides fuzzy search. */ static sqlite3_module spellfix1Module = { - 0, /* iVersion */ - spellfix1Create, /* xCreate - handle CREATE VIRTUAL TABLE */ - spellfix1Connect, /* xConnect - reconnected to an existing table */ - spellfix1BestIndex, /* xBestIndex - figure out how to do a query */ - spellfix1Disconnect, /* xDisconnect - close a connection */ - spellfix1Destroy, /* xDestroy - handle DROP TABLE */ - spellfix1Open, /* xOpen - open a cursor */ - spellfix1Close, /* xClose - close a cursor */ - spellfix1Filter, /* xFilter - configure scan constraints */ - spellfix1Next, /* xNext - advance a cursor */ - spellfix1Eof, /* xEof - check for end of scan */ - spellfix1Column, /* xColumn - read data */ - spellfix1Rowid, /* xRowid - read data */ - spellfix1Update, /* xUpdate */ - 0, /* xBegin */ - 0, /* xSync */ - 0, /* xCommit */ - 0, /* xRollback */ - 0, /* xFindMethod */ - spellfix1Rename, /* xRename */ - 0, /* xSavepoint */ - 0, /* xRelease */ - 0, /* xRollbackTo */ - 0, /* xShadowName */ - 0 /* xIntegrity */ + 0, /* iVersion */ + spellfix1Create, /* xCreate - handle CREATE VIRTUAL TABLE */ + spellfix1Connect, /* xConnect - reconnected to an existing table */ + spellfix1BestIndex, /* xBestIndex - figure out how to do a query */ + spellfix1Disconnect, /* xDisconnect - close a connection */ + spellfix1Destroy, /* xDestroy - handle DROP TABLE */ + spellfix1Open, /* xOpen - open a cursor */ + spellfix1Close, /* xClose - close a cursor */ + spellfix1Filter, /* xFilter - configure scan constraints */ + spellfix1Next, /* xNext - advance a cursor */ + spellfix1Eof, /* xEof - check for end of scan */ + spellfix1Column, /* xColumn - read data */ + spellfix1Rowid, /* xRowid - read data */ + spellfix1Update, /* xUpdate */ + 0, /* xBegin */ + 0, /* xSync */ + 0, /* xCommit */ + 0, /* xRollback */ + 0, /* xFindMethod */ + spellfix1Rename, /* xRename */ + 0, /* xSavepoint */ + 0, /* xRelease */ + 0, /* xRollbackTo */ + 0, /* xShadowName */ + 0 /* xIntegrity */ }; /* ** Register the various functions and the virtual table. */ -static int spellfix1Register(sqlite3 *db){ - int rc = SQLITE_OK; - int i; - rc = sqlite3_create_function(db, "spellfix1_translit", 1, - SQLITE_UTF8|SQLITE_DETERMINISTIC, 0, - transliterateSqlFunc, 0, 0); - if( rc==SQLITE_OK ){ - rc = sqlite3_create_function(db, "spellfix1_editdist", 2, - SQLITE_UTF8|SQLITE_DETERMINISTIC, 0, - editdistSqlFunc, 0, 0); - } - if( rc==SQLITE_OK ){ - rc = sqlite3_create_function(db, "spellfix1_phonehash", 1, +static int spellfix1Register(sqlite3 *db) +{ + int rc = SQLITE_OK; + int i; + rc = sqlite3_create_function(db, "spellfix1_translit", 1, SQLITE_UTF8|SQLITE_DETERMINISTIC, 0, - phoneticHashSqlFunc, 0, 0); - } - if( rc==SQLITE_OK ){ - rc = sqlite3_create_function(db, "spellfix1_scriptcode", 1, - SQLITE_UTF8|SQLITE_DETERMINISTIC, 0, - scriptCodeSqlFunc, 0, 0); - } - if( rc==SQLITE_OK ){ - rc = sqlite3_create_module(db, "spellfix1", &spellfix1Module, 0); - } - if( rc==SQLITE_OK ){ - rc = editDist3Install(db); - } - - /* Verify sanity of the translit[] table */ - for(i=0; idb)); @@ -27,13 +28,15 @@ Data* new_data(const char* con) { return data; } -void free_data(Data* data) { +void free_data(Data* data) +{ sqlite3_close(data->db); free(data); } -void insert(Data* data, char* line, int len) { - sqlite3_stmt *stmt; +void insert(Data* data, char* line, int len) +{ + sqlite3_stmt *stmt; int r = sqlite3_prepare_v2(data->db, insert_into, -1, &stmt, NULL); if (r != SQLITE_OK) { @@ -55,8 +58,9 @@ void insert(Data* data, char* line, int len) { sqlite3_finalize(stmt); } -void bootstrap(Data* data) { - sqlite3_stmt *stmt; +void bootstrap(Data* data) +{ + sqlite3_stmt *stmt; int r = sqlite3_prepare_v2(data->db, create_table, -1, &stmt, NULL); if (r != SQLITE_OK) { @@ -76,8 +80,9 @@ void bootstrap(Data* data) { sqlite3_finalize(stmt); } -LIST* data_select(Data* data, char *sch, int len) { - sqlite3_stmt *stmt; +LIST* data_select(Data* data, char *sch, int len) +{ + sqlite3_stmt *stmt; int r = sqlite3_prepare_v2(data->db, select_words, -1, &stmt, NULL); if (r != SQLITE_OK) { @@ -112,6 +117,7 @@ LIST* data_select(Data* data, char *sch, int len) { return list; } -void print_result_code(int code) { +void print_result_code(int code) +{ printf(sqlite3_errstr(code)); } diff --git a/lib/data.h b/lib/data.h index 56edd34..fcde55a 100644 --- a/lib/data.h +++ b/lib/data.h @@ -5,7 +5,8 @@ /* * This word into the dictionary */ -typedef struct word { +typedef struct word +{ int Id; const unsigned char *Line; } Word; @@ -13,17 +14,16 @@ typedef struct word { /* * This is database connection. */ -typedef struct data { +typedef struct data +{ sqlite3 *db; } Data; - /* * create a new data struct from sqlite filename. */ Data* new_data(const char*); - void free_data(Data*); /* diff --git a/lib/list.c b/lib/list.c index fc0fddf..52feb76 100644 --- a/lib/list.c +++ b/lib/list.c @@ -4,8 +4,7 @@ LIST* list_add(LIST* list, void* item) { - if (list == NULL) - { + if (list == NULL) { list = (LIST*)malloc(sizeof(LIST)); list->size = 0; list->list = (void**)malloc(sizeof(0)); @@ -35,20 +34,21 @@ LIST* list_remove(LIST* list, unsigned int pos) return list; } -void list_free(LIST* list) { - for (unsigned int x = 0; x < list->size; x++) +void list_free(LIST* list) +{ + for (unsigned int x = 0; x < list->size; x++) free(list->list[x]); free(list->list); free(list); } - -void *list_get(LIST *list, unsigned int index) { +void *list_get(LIST *list, unsigned int index) +{ if (list == NULL) return NULL; - if (index < list->size) + if (index < list->size) return list->list[index]; return NULL; diff --git a/lib/list.h b/lib/list.h index dd28722..18bc423 100644 --- a/lib/list.h +++ b/lib/list.h @@ -2,7 +2,8 @@ #include #define LIST_SIZE_FACTOR 1.5 -struct list { +struct list +{ unsigned int size; unsigned int allocated_size; void** list; diff --git a/lib/ui.c b/lib/ui.c index 1a285a0..3eae201 100644 --- a/lib/ui.c +++ b/lib/ui.c @@ -9,7 +9,8 @@ const char *uload = "█"; -PROGRESS_BAR* new_progress_bar(WINDOW* scr, float total) { +PROGRESS_BAR* new_progress_bar(WINDOW* scr, float total) +{ PROGRESS_BAR *bar = (PROGRESS_BAR*)malloc(sizeof(PROGRESS_BAR)); bar->scr = scr; bar->total = total; @@ -17,7 +18,8 @@ PROGRESS_BAR* new_progress_bar(WINDOW* scr, float total) { return bar; } -void bar_step(PROGRESS_BAR* bar, float step){ +void bar_step(PROGRESS_BAR* bar, float step) +{ bar->current += step; int x, y; @@ -42,13 +44,12 @@ void bar_step(PROGRESS_BAR* bar, float step){ wmove(bar->scr, hy+1, hx - len); wprintw(bar->scr, "%.0f/%.0f", bar->current, bar->total); - wmove(bar->scr,0,0); wrefresh(bar->scr); } - -TEXT_BOX* new_text_box(WINDOW* scr, int length) { +TEXT_BOX* new_text_box(WINDOW* scr, int length) +{ TEXT_BOX *text = (TEXT_BOX*)malloc(sizeof(TEXT_BOX)); text->scr = scr; text->length = length; @@ -58,22 +59,23 @@ TEXT_BOX* new_text_box(WINDOW* scr, int length) { return text; } -void get_char(TEXT_BOX* text, void (*sch)(char*, int)) { - while(1){ +void get_char(TEXT_BOX* text, void (*sch)(char*, int)) +{ + while(1) { wchar_t c; get_wch((wint_t*)&c); switch(c) { - case KEY_BACKSPACE: - if (text->current > 0) { - text->text[text->current--] = '\0'; - } - break; - default: - if (text->current < (text->length-2)) { - text->text[text->current] = c; - text->text[++text->current] = '\0'; - } + case KEY_BACKSPACE: + if (text->current > 0) { + text->text[text->current--] = '\0'; + } + break; + default: + if (text->current < (text->length-2)) { + text->text[text->current] = c; + text->text[++text->current] = '\0'; + } } char str[text->length]; diff --git a/lib/ui.h b/lib/ui.h index 90b352f..12ee2f4 100644 --- a/lib/ui.h +++ b/lib/ui.h @@ -1,7 +1,8 @@ #pragma once #include -typedef struct progress_bar { +typedef struct progress_bar +{ float total; float current; WINDOW *scr; @@ -10,8 +11,8 @@ typedef struct progress_bar { PROGRESS_BAR* new_progress_bar(WINDOW*, float); void bar_step(PROGRESS_BAR*, float); - -typedef struct text_box { +typedef struct text_box +{ wchar_t *text; int length; int current; @@ -20,4 +21,3 @@ typedef struct text_box { TEXT_BOX* new_text_box(WINDOW*, int); void get_char(TEXT_BOX* text, void (*sch)(char*, int)); - diff --git a/lib/util.c b/lib/util.c index 6720082..895ca89 100644 --- a/lib/util.c +++ b/lib/util.c @@ -6,7 +6,8 @@ #define BUF_SIZE 100 -char* copy_achar(const char* src) { +char* copy_achar(const char* src) +{ int len = strlen(src) + 1; char* dest = (char*)malloc(sizeof(char)*len); strcpy(dest, src); @@ -14,8 +15,8 @@ char* copy_achar(const char* src) { return dest; } - -int load_or_save_db(sqlite3 *pInMemory, const char *zFilename, int isSave){ +int load_or_save_db(sqlite3 *pInMemory, const char *zFilename, int isSave) +{ int rc; /* Function return code */ sqlite3 *pFile; /* Database connection opened on zFilename */ sqlite3_backup *pBackup; /* Backup object used to copy data */ @@ -23,12 +24,12 @@ int load_or_save_db(sqlite3 *pInMemory, const char *zFilename, int isSave){ sqlite3 *pFrom; /* Database to copy from (pFile or pInMemory) */ rc = sqlite3_open(zFilename, &pFile); - if( rc==SQLITE_OK ){ + if( rc==SQLITE_OK ) { pFrom = (isSave ? pInMemory : pFile); pTo = (isSave ? pFile : pInMemory); pBackup = sqlite3_backup_init(pTo, "main", pFrom, "main"); - if( pBackup ){ + if( pBackup ) { (void)sqlite3_backup_step(pBackup, -1); (void)sqlite3_backup_finish(pBackup); } @@ -39,11 +40,11 @@ int load_or_save_db(sqlite3 *pInMemory, const char *zFilename, int isSave){ return rc; } -unsigned int count_file_lines(FILE *file) { +unsigned int count_file_lines(FILE *file) +{ char buf[BUF_SIZE]; unsigned int counter = 0; - for(;;) - { + for(;;) { size_t res = fread(buf, 1, BUF_SIZE, file); if (ferror(file)) return -1; diff --git a/lib/util.h b/lib/util.h index c03dbae..33c61ed 100644 --- a/lib/util.h +++ b/lib/util.h @@ -8,8 +8,6 @@ */ char* copy_achar(const char*); - int load_or_save_db(sqlite3 *pInMemory, const char *zFilename, int isSave); - unsigned int count_file_lines(FILE *file); -- cgit v1.2.3