From 7f1a0293cc89fd6e28565700d9a496d17cbc6538 Mon Sep 17 00:00:00 2001 From: stranger Date: Tue, 6 Feb 2007 14:08:16 +0000 Subject: [PATCH] Bookmark BDB and XDXF engine has been cleaned from some bugs. git-svn-id: file:///svnroot/mdictionary/trunk@65 5bde0345-f819-0410-ac75-e5045f9217cc --- src/bookmarks/bdb/src/engine_bookmark.c | 16 +- src/plugins/xdxf/src/engine_xdxf.c | 252 ++++++++++++++++++------------- 2 files changed, 158 insertions(+), 110 deletions(-) diff --git a/src/bookmarks/bdb/src/engine_bookmark.c b/src/bookmarks/bdb/src/engine_bookmark.c index fe2e46c..ecb9cc3 100755 --- a/src/bookmarks/bdb/src/engine_bookmark.c +++ b/src/bookmarks/bdb/src/engine_bookmark.c @@ -909,7 +909,8 @@ void bm_engine_search_word_list(Engine* engine, gchar* pattern) { // especiall treat if user give only '*' while(0 == code) { - g_array_append_val(result,search.data); + gchar* cos = g_strdup(search.data); + g_array_append_val(result,cos); code = data->db_words->seq(data->db_words, &search, &reply, R_NEXT); // we are giving to the user all words from dictionary } @@ -925,7 +926,8 @@ void bm_engine_search_word_list(Engine* engine, gchar* pattern) if( 0 == g_utf8_collate( down_word , tmp ) ) { - g_array_append_val(result,search.data); + gchar* cos = g_strdup(search.data); + g_array_append_val(result, cos ); }; //eg_free(reply.data); @@ -933,7 +935,7 @@ void bm_engine_search_word_list(Engine* engine, gchar* pattern) code = data->db_words->seq(data->db_words, &search, &reply, R_NEXT); } } - eg_free(tmp); + eg_free(tmp); tmp = NULL; bm_timer(TIMER_STOP,(gchar*)(gchar*)__FUNCTION__); @@ -941,7 +943,7 @@ void bm_engine_search_word_list(Engine* engine, gchar* pattern) // calling callback for word translation data->cb_search_word_list(result , pattern, data->cb_search_word_list_data, ENGINE_NO_ERROR); - + bm_timer(TIMER_STOP,"callback for returning word LIST END"); if(data->auto_free) { LOGS("Bookmark/%s->%s() deleting all dynamic data because " @@ -949,7 +951,11 @@ void bm_engine_search_word_list(Engine* engine, gchar* pattern) (gchar*)__FILE__, (gchar*)__FUNCTION__ ); - + len = 0; + while(NULL != (tmp = g_array_index(result,gchar*,len++))) + { + g_free(tmp); tmp = NULL; + } g_array_free(result, TRUE); } } diff --git a/src/plugins/xdxf/src/engine_xdxf.c b/src/plugins/xdxf/src/engine_xdxf.c index 11031e6..30072af 100644 --- a/src/plugins/xdxf/src/engine_xdxf.c +++ b/src/plugins/xdxf/src/engine_xdxf.c @@ -199,15 +199,17 @@ void xdxf_engine_search_word_translation(Engine* engine, gchar* word) guint word_length = strlen(word); gchar* trans; - + gchar* casefold_word = g_utf8_casefold(word, -1); // dictionary is optimized if(data->cache != NULL) { - trans = word_translation_cache(data, word); - // dictionary is not optimized right now - } else - { - trans = word_translation_xdxf(data, word); + trans = word_translation_cache(data, casefold_word); + } + else + // dictionary is not optimized right now + { + trans = word_translation_xdxf(data, casefold_word); }; + g_free(casefold_word); casefold_word = NULL; g_debug("XDXF/%s->%s() found for word \'%s\' translation:\n\'%s\'\n", __FILE__, @@ -240,7 +242,7 @@ gboolean xdxf_engine_remove_word(Engine* engine, gchar* word) { g_debug("%s -> %s()", __FILE__, __FUNCTION__); - g_debug("Not implemented yet."); + g_debug("Not implemented yet. And wont be."); return FALSE; } @@ -249,7 +251,7 @@ gboolean xdxf_engine_add_word(Engine* engine, gchar* translation) { g_debug("%s -> %s()", __FILE__, __FUNCTION__); - g_debug("Not implemented yet."); + g_debug("Not implemented yet. And wont be."); return FALSE; } @@ -259,13 +261,20 @@ static void search_word_trans_start(void *data, ) { XDXFWordsTransData* loc_data = (XDXFWordsTransData*)data; - if((loc_data->translation != NULL) || !(loc_data->cont)) { + if( (NULL != loc_data->translation) || !(loc_data->cont) ) + { + //g_debug("STOP SEARCHING! -> _start\n"); return; }; - if(g_utf8_collate(el,"k") == 0) { + if( ('k' == el[0]) && ('\0' == el[1]) ) + { loc_data->one_word = 1; - } else if(g_utf8_collate(el,"ar") == 0) { + loc_data->last_word_length = 0; + loc_data->last_word[0] = '\0'; + } + else if( ('a' == el[0]) && ('r' == el[1]) && ('\0' == el[2]) ) + { loc_data->last_start = XML_GetCurrentByteIndex(*(loc_data->parser)); } @@ -274,26 +283,32 @@ static void search_word_trans_start(void *data, static void search_word_trans_end(void *data, const char *el) { XDXFWordsTransData* loc_data = (XDXFWordsTransData*)data; - if((loc_data->translation != NULL) || !(loc_data->cont)) { + if( (NULL != loc_data->translation) || (FALSE == loc_data->cont) ) { + //g_debug("STOP SEARCHING! -> _end\n"); return; }; - if(g_utf8_collate(el,"k") == 0) { + if( ('k' == el[0]) && ('\0' == el[1]) ) { loc_data->one_word = 0; gint com = g_utf8_collate(loc_data->last_word, loc_data->word); - if(com > 0) { - loc_data->cont = FALSE; + if(com != 0) { + //loc_data->cont = FALSE; + // dictionaries are not alway properly sorted, so for + // searching we should lookd in whole file! return; } else if((loc_data->last_word_length == loc_data->word_len) && - ( com == 0 ) - ) { + (0 == com)) + { loc_data->found = TRUE; - }; - // "clearing" buffer for next word + }; + // "clearing" buffer for next word loc_data->last_word_length = 0; - } - else if((g_utf8_collate(el,"ar") == 0) && (loc_data->found)) { + //loc_data->last_word[0] = '\0'; + } + else if( (TRUE == loc_data->found) && (0 == g_utf8_collate(el,"ar")) ) + { + g_debug("Found!\n"); loc_data->found = FALSE; loc_data->cont = FALSE; gulong last_stop = @@ -303,17 +318,19 @@ static void search_word_trans_end(void *data, const char *el) (last_stop - (loc_data->last_start)) }; loc_data->translation = read_file_part(&fp, loc_data->xdxf); + //g_debug("File part returned from file is: %s\n",loc_data->translation); } } //------------------------------------------------------------------------------ static void search_word_trans_text(void *data, const XML_Char *txt, int len) { XDXFWordsTransData* loc_data = (XDXFWordsTransData*)data; - if((loc_data->translation != NULL) || !(loc_data->cont)) { + if( (NULL != loc_data->translation ) || (FALSE == loc_data->cont) ) { + //g_debug("STOP SEARCHING! -> _text\n"); return; }; - if(loc_data->one_word == 1) { + if(1 == loc_data->one_word) { memcpy(&(loc_data->last_word[loc_data->last_word_length]), (gchar*)txt, len @@ -380,7 +397,7 @@ gpointer xdxf_engine_set_callbacks(Engine* engine, __FUNCTION__, (guint)user_data ); - return result; + return result; } else if(g_ascii_strcasecmp(signal, ENGINE_WORD_LIST_SIGNAL) == 0) { gpointer result = data->cb_search_word_list; @@ -401,7 +418,7 @@ gpointer xdxf_engine_set_callbacks(Engine* engine, __FUNCTION__, (guint)user_data ); - return result; + return result; } else if(g_ascii_strcasecmp(signal, ENGINE_WORD_TRANSLATION_SIGNAL) == 0) { @@ -423,7 +440,7 @@ gpointer xdxf_engine_set_callbacks(Engine* engine, __FUNCTION__, (guint)user_data ); - return result; + return result; } else { g_warning("XDXF/%s->%s() unsupported signal: %s.\n", @@ -447,7 +464,7 @@ void xdxf_engine_close(Engine* engine) __FUNCTION__, engine ); - return; + return; } XDXFData* data = (XDXFData*)(engine->engine_data); if(data->cache != NULL) { @@ -456,7 +473,7 @@ void xdxf_engine_close(Engine* engine) if(data->xdxf != NULL) { gnome_vfs_close(data->xdxf); }; - + g_free(data->dict_path); g_free(data); g_free(engine); @@ -486,11 +503,11 @@ Engine* xdxf_engine_create(gchar* location, location, (guint)auto_cache ); - timer(TIMER_START,(gchar*)__FUNCTION__); + timer(TIMER_START,(gchar*)__FUNCTION__); GnomeVFSResult open_result; if(!gnome_vfs_initialized ()) { - gnome_vfs_init (); + gnome_vfs_init (); }; gchar* tmp = g_strdup(location); @@ -513,11 +530,10 @@ Engine* xdxf_engine_create(gchar* location, // API 0.2 result->engine_add_word = xdxf_engine_add_word; result->engine_remove_word = xdxf_engine_remove_word; - + XDXFData* data = (XDXFData*)g_try_malloc(sizeof(XDXFData)); result->engine_data = (gpointer)data; - - + g_debug("XDXF/%s->%s() opening file...\'%s\'.\n", __FILE__, __FUNCTION__, @@ -585,9 +601,9 @@ Engine* xdxf_engine_create(gchar* location, }; } g_free(tmp); tmp = NULL; - + timer(TIMER_STOP,(gchar*)__FUNCTION__); - g_debug("XDXF/%s->%s() returned Engine at adress=%p\n TO NAPEWNO TEN PLIK", + g_debug("XDXF/%s->%s() returned Engine at adress=%p\n", __FILE__, __FUNCTION__, result @@ -623,7 +639,7 @@ static double timer(gboolean start, gchar* message) if(first_run) { first_run = FALSE; stack = g_array_new(TRUE, TRUE, sizeof(struct timeval)); - }; + }; if (start) { g_debug("XDXF->%s() start counting time for function '%s()'.\n", @@ -636,7 +652,7 @@ static double timer(gboolean start, gchar* message) } // we just want to end some timer - print some information about // working time; - else { + else { gettimeofday(&actual_time,NULL); last_time = g_array_index(stack, struct timeval, 0); g_array_remove_index(stack, 0); @@ -681,9 +697,9 @@ static gchar* read_file_part(FilePart* part, GnomeVFSHandle* file) timer(TIMER_START,(gchar*)__FUNCTION__); gchar* result = NULL; GnomeVFSResult f_result; - GnomeVFSFileSize bytes_read; - - f_result = gnome_vfs_seek(file, GNOME_VFS_SEEK_START, part->offset); + GnomeVFSFileSize bytes_read; + + f_result = gnome_vfs_seek(file, GNOME_VFS_SEEK_START, part->offset); if(f_result != GNOME_VFS_OK) { g_warning("XDXF/%s->%s() failed. Not possible to seek " "through file!\n", @@ -692,7 +708,6 @@ static gchar* read_file_part(FilePart* part, GnomeVFSHandle* file) ); timer(TIMER_STOP,(gchar*)__FUNCTION__); return result; - }; result = g_try_malloc((part->length + 1) * sizeof(gchar)); if(result == NULL) { @@ -714,7 +729,7 @@ static gchar* read_file_part(FilePart* part, GnomeVFSHandle* file) ); timer(TIMER_STOP,(gchar*)__FUNCTION__); g_free(result); result = NULL; - return result; + return result; }; result[part->length] = '\0'; @@ -812,7 +827,7 @@ static gboolean is_xdxf_file(gchar* file) { GnomeVFSFileSize bytes_read; if(!gnome_vfs_initialized ()) { - gnome_vfs_init (); + gnome_vfs_init (); }; file_result = gnome_vfs_open (&fd, file, GNOME_VFS_OPEN_READ); @@ -837,7 +852,7 @@ static gboolean is_xdxf_file(gchar* file) { XML_SetElementHandler(p, is_xdxf_file_start, is_xdxf_file_end); XDXFCheckingData user_data = {TRUE, FALSE, 0}; XML_SetUserData(p, &user_data); - gchar buffer[DICT_CACHEING_BUFF_SIZE]; + gchar buffer[DICT_CACHEING_BUFF_SIZE]; guint loop_count = 0; while(TRUE) { @@ -854,7 +869,7 @@ static gboolean is_xdxf_file(gchar* file) { __FUNCTION__ ); break; - }; + }; if (! XML_Parse(p, buffer, (gulong)bytes_read, @@ -903,7 +918,7 @@ static void is_xdxf_file_start(void *data, const char *el, const char **attr) XDXFCheckingData* user_data = (XDXFCheckingData*)data; if (user_data->deep == 0) { if (g_utf8_collate (el,"xdxf") != 0) { - user_data->good = FALSE; + user_data->good = FALSE; } else { user_data->good = TRUE; @@ -940,14 +955,14 @@ static void caching_expat_start(void *data, const char *el, const char **attr) { loc_data->state = 1; } else { - loc_data->state = 0; + //loc_data->state = 0; } } //------------------------------------------------------------------------------ static void caching_expat_end(void *data, const char *el) { XDXFCacheData* loc_data = (XDXFCacheData*)data; loc_data->last_stop = XML_GetCurrentByteIndex(loc_data->parser); - + static guint record_length; static guint start; static guint length; @@ -964,7 +979,7 @@ static void caching_expat_end(void *data, const char *el) { start = loc_data->last_start; length = loc_data->last_stop + strlen("") - loc_data->last_start; - + gboolean error_writting = FALSE; GnomeVFSFileSize bytes_written; GnomeVFSResult vfs_result; @@ -992,7 +1007,7 @@ static void caching_expat_end(void *data, const char *el) { &bytes_written ); if(vfs_result != GNOME_VFS_OK) error_writting = TRUE; - + loc_data->buffer[0] = '\0'; loc_data->buffer_length = 0; loc_data->state = 0; @@ -1025,7 +1040,7 @@ static guint64 get_file_size(GnomeVFSHandle* file) if( gnome_vfs_tell(file, &result) != GNOME_VFS_OK) { result = 0; } - + gnome_vfs_seek(file, GNOME_VFS_SEEK_START, old_pos); return result; } @@ -1038,7 +1053,7 @@ void xdxf_engine_optimize(Engine* engine) engine ); timer(TIMER_START,(gchar*)__FUNCTION__); - + GnomeVFSResult vfs_result; XDXFData* data = (XDXFData*)(engine->engine_data); gchar* cache_path = g_strconcat(data->dict_path,"/dict.cache",NULL); @@ -1084,7 +1099,7 @@ void xdxf_engine_optimize(Engine* engine) caching_expat_end ); XML_SetCharacterDataHandler(c_data->parser, caching_expat_text); - + GnomeVFSFileSize bytes_readed = DICT_CACHEING_BUFF_SIZE; gchar b[DICT_CACHEING_BUFF_SIZE + 1]; gdouble last_prog = 0; @@ -1099,7 +1114,7 @@ void xdxf_engine_optimize(Engine* engine) bytes_readed, bytes_readed < DICT_CACHEING_BUFF_SIZE ); - + if(data->cb_progress_caching != NULL) { GnomeVFSFileSize act_pos; gnome_vfs_tell(data->xdxf, &act_pos); @@ -1134,7 +1149,7 @@ void xdxf_engine_optimize(Engine* engine) } //------------------------------------------------------------------------------ gboolean xdxf_engine_check(gchar* location) -{ +{ g_debug("XDXF/%s->%s() called.\n-->PARAM:location=\'%s\'\n", __FILE__, __FUNCTION__, @@ -1144,7 +1159,7 @@ gboolean xdxf_engine_check(gchar* location) gboolean result = TRUE; gchar* filepath = g_strdup(location); gchar* tmp = NULL; - + string_to_path(&filepath); if (filepath == NULL) { result = FALSE; @@ -1314,51 +1329,63 @@ static void search_word_list_start(void *data, ) { XDXFWordsListData* loc_data = (XDXFWordsListData*)data; - if(g_utf8_collate(el,"k") == 0) { + if( ('k' == el[0]) && ('\0' == el[1]) ) { loc_data->one_word = 1; }; } //------------------------------------------------------------------------------ static void search_word_list_end(void *data, const char *el) { + static gint compare_result = 0; + static gboolean matched = FALSE; + static gchar* tmp = NULL; + XDXFWordsListData* loc_data = (XDXFWordsListData*)data; - if(g_utf8_collate(el,"k") == 0) { + if( ('k' == el[0]) && ('\0' == el[1]) ) + { loc_data->one_word = 0; } - else { + else + { return; } - static gboolean any_found = FALSE; - gboolean matched = FALSE; - if(( loc_data->last_word_length >= loc_data->pattern_len ) && - (g_ascii_strncasecmp(loc_data->last_word, - loc_data->pattern, - loc_data->pattern_len) == 0)) { + tmp = g_utf8_casefold(loc_data->last_word, -1); + + if(loc_data->last_word_length > loc_data->pattern_len) + { + tmp[loc_data->pattern_len] = '\0'; + }; + compare_result = g_utf8_collate(tmp, loc_data->pattern); + + if( ( loc_data->last_word_length >= loc_data->pattern_len ) && + ( compare_result == 0 ) ) + { matched = TRUE; - any_found = TRUE; gchar* new = g_strdup(loc_data->last_word); g_array_append_val((loc_data->result), new); g_debug("New Word for pattern \"%s\" found: %s\n", loc_data->pattern, new ); - }; + } + else { + matched = FALSE; + } // "clearing" buffer for next word loc_data->last_word_length = 0; // if we passed words matching -> ends - if(any_found && !matched) { + if( (loc_data->result->len > 0) && (!matched) ) { loc_data->cont = FALSE; }; - matched = FALSE; - any_found = FALSE; + g_free(tmp); tmp = NULL; } //------------------------------------------------------------------------------ static void search_word_list_text(void *data, const XML_Char *txt, int len) { XDXFWordsListData* loc_data = (XDXFWordsListData*)data; - if(loc_data->one_word == 1) { + if(1 == loc_data->one_word) { memcpy(&(loc_data->last_word[loc_data->last_word_length]), (gchar*)txt, len @@ -1380,13 +1407,15 @@ static void word_list_cache(XDXFData* data, gchar* pattern, GArray* result) { guint max_length = 0; guint64 file_size = get_file_size(data->cache); guint pattern_len = strlen(pattern); - while(TRUE) { + + while(TRUE) + { gnome_vfs_read(data->cache, buffer, DICT_SEARCHING_WORD_LIST_BUFF_SIZE, &bytes_readed ); - + max_length = get_max_length(buffer, (guint)bytes_readed); already += max_length; buf = buffer; @@ -1411,40 +1440,43 @@ static void word_list_cache(XDXFData* data, gchar* pattern, GArray* result) { }; how_far += record_length; buf = buf + record_length; - + } if( (bytes_readed < DICT_SEARCHING_WORD_LIST_BUFF_SIZE) || - (already > (file_size -3)) ) { - break; - } - gnome_vfs_seek(data->cache, - GNOME_VFS_SEEK_CURRENT, - ((gint)max_length) - - DICT_SEARCHING_WORD_LIST_BUFF_SIZE - ); + (already > (file_size -3)) ) + { + break; + } + gnome_vfs_seek(data->cache, + GNOME_VFS_SEEK_CURRENT, + (gint)max_length - + DICT_SEARCHING_WORD_LIST_BUFF_SIZE + ); } timer(TIMER_STOP,(gchar*)__FUNCTION__); - timer(TIMER_START,"callback for returning words list START"); + timer(TIMER_START,"callback for returning words list START"); data->cb_search_word_list(result, pattern, data->cb_search_word_list_data, ENGINE_NO_ERROR ); - timer(TIMER_STOP,"callback for returning words list END"); + timer(TIMER_STOP,"callback for returning words list END"); } //------------------------------------------------------------------------------ // return translation of word but using only xdxf dictionary file -static void word_list_xdxf(XDXFData* data, gchar* pattern, GArray* result) { +static void word_list_xdxf(XDXFData* data, gchar* pattern, GArray* result) +{ gnome_vfs_seek(data->xdxf, GNOME_VFS_SEEK_START, 0); GnomeVFSResult vfs_result; - GnomeVFSFileSize bytes_readed = DICT_SEARCHING_WORD_LIST_BUFF_SIZE; - gchar buffer[DICT_SEARCHING_WORD_LIST_BUFF_SIZE+1]; + GnomeVFSFileSize bytes_readed = DICT_SEARCHING_WORD_LIST_BUFF_SIZE; + gchar buffer[DICT_SEARCHING_WORD_LIST_BUFF_SIZE+1]; guint64 file_size = get_file_size(data->xdxf); - guint pattern_len = strlen(pattern); + gchar* casefold_pattern = g_utf8_casefold(pattern, -1); + guint pattern_len = strlen(casefold_pattern); + - - XML_Parser parser = XML_ParserCreate(NULL); + XML_Parser parser = XML_ParserCreate(NULL); if (!parser) { g_warning("XDXF/%s->%s() Could not open initialize XML " "parser.\n", @@ -1461,7 +1493,7 @@ static void word_list_xdxf(XDXFData* data, gchar* pattern, GArray* result) { search_word_list_end ); XML_SetCharacterDataHandler(parser, search_word_list_text); - + // buffer for single word // pattern to search // length of pattern @@ -1470,7 +1502,7 @@ static void word_list_xdxf(XDXFData* data, gchar* pattern, GArray* result) { // continuation of the same word // continue of searching? XDXFWordsListData search_data = {tmp, - pattern, + casefold_pattern, pattern_len, 0, result, @@ -1491,8 +1523,9 @@ static void word_list_xdxf(XDXFData* data, gchar* pattern, GArray* result) { bytes_readed, bytes_readed < DICT_SEARCHING_WORD_LIST_BUFF_SIZE ); - - if(data->cb_progress_word_list != NULL) { + + if(NULL != data->cb_progress_word_list) + { GnomeVFSFileSize act_pos; gnome_vfs_tell(data->xdxf, &act_pos); gdouble progress = ((gdouble)act_pos)/ @@ -1507,11 +1540,15 @@ static void word_list_xdxf(XDXFData* data, gchar* pattern, GArray* result) { ); last_prog = progress; }; - } - if(bytes_readed < DICT_SEARCHING_WORD_LIST_BUFF_SIZE) { + }; + + if(bytes_readed < DICT_SEARCHING_WORD_LIST_BUFF_SIZE) + { break; - } - if((search_data.cont) == FALSE) { + }; + + if(FALSE == search_data.cont) + { g_debug("XDXF/%s->%s() We found every words matching " "pattern \"%s\". Abort further searching.\n", __FILE__, @@ -1519,9 +1556,10 @@ static void word_list_xdxf(XDXFData* data, gchar* pattern, GArray* result) { pattern ); break; - } + }; } XML_ParserFree(parser); + g_free(casefold_pattern); casefold_pattern = NULL; timer(TIMER_STOP,(gchar*)__FUNCTION__); timer(TIMER_START,"callback for returning words list START"); @@ -1530,7 +1568,7 @@ static void word_list_xdxf(XDXFData* data, gchar* pattern, GArray* result) { data->cb_search_word_list_data, ENGINE_NO_ERROR ); - timer(TIMER_STOP,"callback for returning words list END"); + timer(TIMER_STOP,"callback for returning words list END"); } //------------------------------------------------------------------------------ void xdxf_engine_search_word_list(Engine* engine, gchar* pattern) @@ -1548,7 +1586,8 @@ void xdxf_engine_search_word_list(Engine* engine, gchar* pattern) timer(TIMER_START,(gchar*)__FUNCTION__); XDXFData* data = (XDXFData*)(engine->engine_data); - if(data->cb_search_word_list == NULL) { + if(data->cb_search_word_list == NULL) + { g_warning("XDXF/%s->%s() callback for Word List not set. " "Searching aborted.\n", __FILE__, @@ -1559,15 +1598,18 @@ void xdxf_engine_search_word_list(Engine* engine, gchar* pattern) }; GArray* result = g_array_new(TRUE,FALSE,sizeof(gchar*)); // dictionary is optimized so search in cache file - if(data->cache != NULL) { + if(data->cache != NULL) + { word_list_cache(data, pattern, result); } // dictionary is not optimized so search directly fom XDXF file - else { + else + { word_list_xdxf(data, pattern, result); }; - - if(data->auto_free == TRUE) { + + if(data->auto_free == TRUE) + { g_debug("XDXF/%s->%s() deleting all dynamic data because " "AUTO_FREE=TRUE\n", __FILE__, -- 1.7.9.5