source: trunk/third/evolution/libibex/ibex_block.c @ 16787

Revision 16787, 13.8 KB checked in by ghudson, 23 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r16786, which included commits to RCS files with non-trunk default branches.
Line 
1
2#ifdef HAVE_CONFIG_H
3#include <config.h>
4#endif
5
6#include <glib.h>
7
8#include <stdio.h>
9#include <gal/unicode/gunicode.h>
10#include <ctype.h>
11#include <string.h>
12#include <errno.h>
13#include <stdlib.h>
14
15#include <sys/types.h>
16#include <sys/stat.h>
17#include <unistd.h>
18
19#ifdef HAVE_ALLOCA_H
20#include <alloca.h>
21#endif
22
23#include "ibex_internal.h"
24
25#define d(x)
26#define o(x)
27
28static void ibex_reset(ibex *ib);
29static int close_backend(ibex *ib);
30
31static struct _list ibex_list = { (struct _listnode *)&ibex_list.tail, 0, (struct _listnode *)&ibex_list.head };
32static int ibex_open_init = 0;
33static int ibex_open_threshold = IBEX_OPEN_THRESHOLD;
34
35#ifdef ENABLE_THREADS
36#include <pthread.h>
37static pthread_mutex_t ibex_list_lock = PTHREAD_MUTEX_INITIALIZER;
38static int ibex_opened;         /* count of actually opened ibexe's */
39#define IBEX_LIST_LOCK(ib) (pthread_mutex_lock(&ibex_list_lock))
40#define IBEX_LIST_UNLOCK(ib) (pthread_mutex_unlock(&ibex_list_lock))
41#else
42#define IBEX_LIST_LOCK(ib)
43#define IBEX_LIST_UNLOCK(ib)
44#endif
45
46/* check we are open properly */
47
48/* TODO: return errors? */
49static void ibex_use(ibex *ib)
50{
51        ibex *wb, *wn;
52
53        /* always lock list then ibex */
54        IBEX_LIST_LOCK(ib);
55
56        if (ib->blocks == NULL) {
57                o(printf("Delayed opening ibex '%s', total = %d\n", ib->name, ibex_opened+1));
58
59                ib->blocks = ibex_block_cache_open(ib->name, ib->flags, ib->mode);
60                if (ib->blocks) {
61                        /* FIXME: the blockcache or the wordindex needs to manage the other one */
62                        ib->words = ib->blocks->words;         
63                } else {
64                        ib->words = NULL;
65                        g_warning("ibex_use:open(): Error occured?: %s\n", strerror(errno));
66                }
67                if (ib->blocks != NULL)
68                        ibex_opened++;
69        }
70
71        ib->usecount++;
72
73        /* this makes an 'lru' cache of used files */
74        ibex_list_remove((struct _listnode *)ib);
75        ibex_list_addtail(&ibex_list, (struct _listnode *)ib);
76
77        /* check env variable override for open threshold */
78        if (!ibex_open_init) {
79                char *limit;
80
81                ibex_open_init = TRUE;
82                limit = getenv("IBEX_OPEN_THRESHOLD");
83                if (limit) {
84                        ibex_open_threshold = atoi(limit);
85                        if (ibex_open_threshold < IBEX_OPEN_THRESHOLD)
86                                ibex_open_threshold = IBEX_OPEN_THRESHOLD;
87                }
88        }
89
90        /* check for other ibex's we can close now to not over-use fd's.
91           we can't do this first for locking issues */
92        wb = (ibex *)ibex_list.head;
93        wn = wb->next;
94        while (wn && ibex_opened > ibex_open_threshold) {
95                if (wb != ib && IBEX_TRYLOCK(wb) == 0) {
96                        if (wb->usecount == 0 && wb->blocks != NULL) {
97                                o(printf("Forcing close of obex '%s', total = %d\n", wb->name, ibex_opened-1));
98                                close_backend(wb);
99                                ibex_opened--;
100                        }
101                        IBEX_UNLOCK(wb);
102                }
103                wb = wn;
104                wn = wn->next;
105        }
106
107        IBEX_LIST_UNLOCK(ib);
108}
109
110static void ibex_unuse(ibex *ib)
111{
112        ib->usecount--;
113}
114
115static signed char utf8_trans[] = {
116        'A', 'A', 'A', 'A', 'A', 'A', -1, 'C', 'E', 'E', 'E', 'E', 'I', 'I',
117        'I', 'I', -2, 'N', 'O', 'O', 'O', 'O', 'O', '*', 'O', 'U', 'U', 'U',
118        'U', 'Y', -3, -4, 'a', 'a', 'a', 'a', 'a', 'a', -5, 'c', 'e', 'e',
119        'e', 'e', 'i', 'i', 'i', 'i', -6, 'n', 'o', 'o', 'o', 'o', 'o', '/',
120        'o', 'u', 'u', 'u', 'u', 'y', -7, 'y', 'A', 'a', 'A', 'a', 'A', 'a',
121        'C', 'c', 'C', 'c', 'C', 'c', 'C', 'c', 'D', 'd', 'D', 'd', 'E', 'e',
122        'E', 'e', 'E', 'e', 'E', 'e', 'E', 'e', 'G', 'g', 'G', 'g', 'G', 'g',
123        'G', 'g', 'H', 'h', 'H', 'h', 'I', 'i', 'I', 'i', 'I', 'i', 'I', 'i',
124        'I', 'i', -8, -9, 'J', 'j', 'K', 'k', 'k', 'L', 'l', 'L', 'l', 'L',
125        'l', 'L', 'l', 'L', 'l', 'N', 'n', 'N', 'n', 'N', 'n', 'n', -10, -11,
126        'O', 'o', 'O', 'o', 'O', 'o', -12, -13, 'R', 'r', 'R', 'r', 'R', 'r',
127        'S', 'r', 'S', 's', 'S', 's', 'S', 's', 'T', 't', 'T', 't', 'T', 't',
128        'U', 'u', 'U', 'u', 'U', 'u', 'U', 'u', 'U', 'u', 'U', 'u', 'W', 'w',
129        'Y', 'y', 'Y', 'Z', 'z', 'Z', 'z', 'Z', 'z', 's'
130};
131
132static char *utf8_long_trans[] = {
133        "AE", "TH", "TH", "ss", "ae", "th", "th", "IJ", "ij",
134        "NG", "ng", "OE", "oe"
135};
136
137/* This is a bit weird. It takes pointers to the start and end (actually
138 * just past the end) of a UTF-8-encoded word, and a buffer at least 1
139 * byte longer than the length of the word. It copies the word into the
140 * buffer in all lowercase without accents, and splits up ligatures.
141 * (Since any ligature would be a multi-byte character in UTF-8, splitting
142 * them into two US-ASCII characters won't overrun the buffer.)
143 *
144 * It is not safe to call this routine with bad UTF-8.
145 */
146static void
147ibex_normalise_word(char *start, char *end, char *buf)
148{
149        unsigned char *s, *d;
150        gunichar uc;
151
152        s = (unsigned char *)start;
153        d = (unsigned char *)buf;
154        while (s < (unsigned char *)end) {
155                if (*s < 0x80) {
156                        /* US-ASCII character: copy unless it's
157                         * an apostrophe.
158                         */
159                        if (*s != '\'')
160                                *d++ = tolower (*s);
161                        s++;
162                } else {
163                        char *next = g_utf8_next_char (s);
164                        uc = g_utf8_get_char (s);
165                        if (uc >= 0xc0 && uc < 0xc0 + sizeof (utf8_trans)) {
166                                signed char ch = utf8_trans[uc - 0xc0];
167                                if (ch > 0)
168                                        *d++ = tolower (ch);
169                                else {
170                                        *d++ = tolower (utf8_long_trans[-ch - 1][0]);
171                                        *d++ = tolower (utf8_long_trans[-ch - 1][1]);
172                                }
173                                s = next;
174                        } else {
175                                while (s < (unsigned char *)next)
176                                        *d++ = *s++;
177                        }
178                }
179        }
180        *d = '\0';
181}
182
183enum { IBEX_ALPHA, IBEX_NONALPHA, IBEX_INVALID, IBEX_INCOMPLETE };
184
185static int
186utf8_category (char *p, char **np, char *end)
187{
188        if (isascii ((unsigned char)*p)) {
189                *np = p + 1;
190                if (isalpha ((unsigned char)*p) || *p == '\'')
191                        return IBEX_ALPHA;
192                return IBEX_NONALPHA;
193        } else {
194                gunichar uc;
195
196                *np = g_utf8_find_next_char (p, end);
197                if (!*np)
198                        return IBEX_INCOMPLETE;
199                uc = g_utf8_get_char (p);
200                if (uc == (gunichar) -1)
201                        return IBEX_INVALID;
202                else if (g_unichar_isalpha (uc))
203                        return IBEX_ALPHA;
204                else
205                        return IBEX_NONALPHA;
206        }
207}
208
209/**
210 * ibex_index_buffer: the lowest-level ibex indexing interface
211 * @ib: an ibex
212 * @name: the name of the file being indexed
213 * @buffer: a buffer containing data from the file
214 * @len: the length of @buffer
215 * @unread: an output argument containing the number of unread bytes
216 *
217 * This routine indexes up to @len bytes from @buffer into @ib.
218 * If @unread is NULL, the indexer assumes that the buffer ends on a
219 * word boundary, and will index all the way to the end of the
220 * buffer. If @unread is not NULL, and the buffer ends with an
221 * alphabetic character, the indexer will assume that the buffer has
222 * been cut off in the middle of a word, and return the number of
223 * un-indexed bytes at the end of the buffer in *@unread. The caller
224 * should then read in more data through whatever means it has
225 * and pass in the unread bytes from the original buffer, followed
226 * by the new data, on its next call.
227 *
228 * Return value: 0 on success, -1 on failure.
229 **/
230int
231ibex_index_buffer (ibex *ib, char *name, char *buffer, size_t len, size_t *unread)
232{
233        char *p, *q, *nq, *end;
234        char *word;
235        int wordsiz, cat = 0;
236        GHashTable *words = g_hash_table_new(g_str_hash, g_str_equal);
237        GPtrArray *wordlist = g_ptr_array_new();
238        int i, ret=-1;
239
240        if (unread)
241                *unread = 0;
242
243        end = buffer + len;
244        wordsiz = 20;
245        word = g_malloc (wordsiz);
246
247        p = buffer;
248        while (p < end) {
249                while (p < end) {
250                        cat = utf8_category (p, &q, end);
251                        if (cat != IBEX_NONALPHA)
252                                break;
253                        p = q;
254                }
255                if (p == end) {
256                        goto done;
257                } else if (cat == IBEX_INVALID) {
258                        goto error;
259                } else if (cat == IBEX_INCOMPLETE)
260                        q = end;
261
262                while (q < end) {
263                        cat = utf8_category (q, &nq, end);
264                        if (cat != IBEX_ALPHA)
265                                break;
266                        q = nq;
267                }
268                if (cat == IBEX_INVALID ||
269                    (cat == IBEX_INCOMPLETE && !unread)) {
270                        goto error;
271                } else if (cat == IBEX_INCOMPLETE || (q == end && unread)) {
272                        *unread = end - p;
273                        goto done;
274                }
275
276                if (wordsiz < q - p + 1) {
277                        wordsiz = q - p + 1;
278                        word = g_realloc (word, wordsiz);
279                }
280                ibex_normalise_word (p, q, word);
281                if (word[0]) {
282                        if (g_hash_table_lookup(words, word) == 0) {
283                                char *newword = g_strdup(word);
284                                g_ptr_array_add(wordlist, newword);
285                                g_hash_table_insert(words, newword, name);
286                        }
287                }
288                p = q;
289        }
290done:
291        /* this weird word=NULL shit is to get rid of compiler warnings about clobbering with longjmp */
292        g_free(word);
293        word = NULL;
294
295        IBEX_LOCK(ib);
296
297        ibex_use(ib);
298
299        if (ibex_block_cache_setjmp(ib->blocks) != 0) {
300                printf("Error in indexing\n");
301                ret = -1;
302                ibex_reset(ib);
303                word = NULL;    /* here too */
304        } else {
305                word = NULL;    /* ... and here */
306                d(printf("name %s count %d size %d\n", name, wordlist->len, len));
307                if (!ib->predone) {
308                        ib->words->klass->index_pre(ib->words);
309                        ib->predone = TRUE;
310                }
311                ib->words->klass->add_list(ib->words, name, wordlist);
312                ret = 0;
313        }
314
315        ibex_unuse(ib);
316
317        IBEX_UNLOCK(ib);
318
319error:
320        for (i=0;i<wordlist->len;i++)
321                g_free(wordlist->pdata[i]);
322        g_ptr_array_free(wordlist, TRUE);
323        g_hash_table_destroy(words);
324        g_free(word);
325        return ret;
326}
327
328
329/**
330 * ibex_open:
331 * @file:
332 * @flags:
333 * @mode:
334 *
335 * Open a new ibex file.  file, flags, and mode as for open(2)
336 *
337 * Return value: A new ibex, or NULL on failure.
338 **/
339ibex *ibex_open (char *file, int flags, int mode)
340{
341        ibex *ib;
342
343        ib = g_malloc0(sizeof(*ib));
344        ib->blocks = NULL;
345        ib->usecount = 0;
346#if 0
347        ib->blocks = ibex_block_cache_open(file, flags, mode);
348        if (ib->blocks == 0) {
349                g_warning("create: Error occured?: %s\n", strerror(errno));
350                g_free(ib);
351                return NULL;
352        }
353        /* FIXME: the blockcache or the wordindex needs to manage the other one */
354        ib->words = ib->blocks->words;
355#endif
356        ib->name = g_strdup(file);
357        ib->flags = flags;
358        ib->mode = mode;
359
360#ifdef ENABLE_THREADS
361        pthread_mutex_init(&ib->lock, NULL);
362#endif
363
364        IBEX_LIST_LOCK(ib);
365        ibex_list_addtail(&ibex_list, (struct _listnode *)ib);
366        IBEX_LIST_UNLOCK(ib);
367
368        return ib;
369}
370
371/**
372 * ibex_save:
373 * @ib:
374 *
375 * Save (sync) an ibex file to disk.
376 *
377 * Return value: -1 on failure.
378 **/
379int ibex_save (ibex *ib)
380{
381        int ret;
382
383        d(printf("syncing database\n"));
384
385        IBEX_LOCK(ib);
386
387        ibex_use(ib);
388
389        if (ibex_block_cache_setjmp(ib->blocks) != 0) {
390                ibex_reset(ib);
391                printf("Error saving\n");
392                ret = -1;
393        } else {
394                if (ib->predone) {
395                        ib->words->klass->index_post(ib->words);
396                        ib->predone = FALSE;
397                }
398                ib->words->klass->sync(ib->words);
399                /* FIXME: some return */
400                ibex_block_cache_sync(ib->blocks);
401                ret = 0;
402        }
403
404        ibex_unuse(ib);
405
406        IBEX_UNLOCK(ib);
407
408        return ret;
409}
410
411static int
412close_backend(ibex *ib)
413{
414        int ret;
415
416        if (ib->blocks == 0)
417                return 0;
418
419        if (ibex_block_cache_setjmp(ib->blocks) != 0) {
420                printf("Error closing!\n");
421                ret = -1;
422        } else {
423                if (ib->predone) {
424                        ib->words->klass->index_post(ib->words);
425                        ib->predone = FALSE;
426                }
427
428                ib->words->klass->close(ib->words);
429                /* FIXME: return */
430                ibex_block_cache_close(ib->blocks);
431                ib->blocks = NULL;
432                ret = 0;
433        }
434
435        return ret;
436}
437
438/* close/reopen the ibex file, assumes we have lock */
439static void
440ibex_reset(ibex *ib)
441{
442        g_warning("resetting ibex file");
443
444        close_backend(ib);
445
446        ib->blocks = ibex_block_cache_open(ib->name, ib->flags, ib->mode);
447        if (ib->blocks == 0) {
448                g_warning("ibex_reset create: Error occured?: %s\n", strerror(errno));
449        } else {
450                /* FIXME: the blockcache or the wordindex needs to manage the other one */
451                ib->words = ib->blocks->words;
452        }
453}
454
455/**
456 * ibex_close:
457 * @ib:
458 *
459 * Close (and save) an ibex file, restoring all resources used.
460 *
461 * Return value: -1 on error.  In either case, ibex is no longer
462 * defined afterwards.
463 **/
464int ibex_close (ibex *ib)
465{
466        int ret;
467
468        d(printf("closing database\n"));
469
470        g_assert(ib->usecount == 0);
471
472        IBEX_LIST_LOCK(ib);
473        ibex_list_remove((struct _listnode *)ib);
474        IBEX_LIST_UNLOCK(ib);
475
476        if (ib->blocks != NULL)
477                ret = close_backend(ib);
478        else
479                ret = 0;
480
481        g_free(ib->name);
482
483#ifdef ENABLE_THREADS
484        pthread_mutex_destroy(&ib->lock);
485#endif
486
487        g_free(ib);
488
489        return ret;
490}
491
492/* rename/move the ibex file */
493int ibex_move(ibex *ib, const char *newname)
494{
495        int ret = 0, error = 0;
496        struct stat st;
497
498        IBEX_LOCK(ib);
499
500        if (ib->blocks)
501                close_backend(ib);
502
503        if (stat(ib->name, &st) == -1 && errno == ENOENT) {
504                error = 0;
505        } else if (rename(ib->name, newname) == -1) {
506                g_warning("could not rename ibex file '%s' to '%s': '%s'", ib->name, newname, strerror(errno));
507                ret = -1;
508                error = errno;
509        }
510
511        g_free(ib->name);
512        ib->name = g_strdup(newname);
513
514        IBEX_UNLOCK(ib);
515
516        if (ret == -1)
517                errno = error;
518
519        return ret;
520}
521
522
523/**
524 * ibex_unindex:
525 * @ib:
526 * @name:
527 *
528 * Remove a name from the index.
529 **/
530void ibex_unindex (ibex *ib, char *name)
531{
532        d(printf("trying to unindex '%s'\n", name));
533
534        IBEX_LOCK(ib);
535
536        ibex_use(ib);
537
538        if (ibex_block_cache_setjmp(ib->blocks) != 0) {
539                printf("Error unindexing!\n");
540                ibex_reset(ib);
541        } else {
542                ib->words->klass->unindex_name(ib->words, name);
543        }
544
545        ibex_unuse(ib);
546
547        IBEX_UNLOCK(ib);
548}
549
550/**
551 * ibex_find:
552 * @ib:
553 * @word:
554 *
555 * Find all names containing @word.
556 *
557 * Return value:
558 **/
559GPtrArray *ibex_find (ibex *ib, char *word)
560{
561        char *normal;
562        int len;
563        GPtrArray *ret;
564
565        len = strlen(word);
566        normal = alloca(len+1);
567        ibex_normalise_word(word, word+len, normal);
568
569        IBEX_LOCK(ib);
570        ibex_use(ib);
571
572        if (ibex_block_cache_setjmp(ib->blocks) != 0) {
573                ibex_reset(ib);
574                ret = NULL;
575        } else {
576                ret = ib->words->klass->find(ib->words, normal);
577        }
578
579        ibex_unuse(ib);
580        IBEX_UNLOCK(ib);
581
582        return ret;
583}
584
585/**
586 * ibex_find_name:
587 * @ib:
588 * @name:
589 * @word:
590 *
591 * Return #TRUE if the specific @word is contained in @name.
592 *
593 * Return value:
594 **/
595gboolean ibex_find_name (ibex *ib, char *name, char *word)
596{
597        char *normal;
598        int len;
599        gboolean ret;
600
601        len = strlen(word);
602        normal = alloca(len+1);
603        ibex_normalise_word(word, word+len, normal);
604
605        IBEX_LOCK(ib);
606        ibex_use(ib);
607
608        if (ibex_block_cache_setjmp(ib->blocks) != 0) {
609                ibex_reset(ib);
610                ret = FALSE;
611        } else {
612                ret = ib->words->klass->find_name(ib->words, name, normal);
613        }
614
615        ibex_unuse(ib);
616        IBEX_UNLOCK(ib);
617
618        return ret;
619}
620
621/**
622 * ibex_contains_name:
623 * @ib:
624 * @name:
625 *
626 * Returns true if the name @name is somewhere in the database.
627 *
628 * Return value:
629 **/
630gboolean ibex_contains_name(ibex *ib, char *name)
631{
632        gboolean ret;
633
634        IBEX_LOCK(ib);
635        ibex_use(ib);
636
637        if (ibex_block_cache_setjmp(ib->blocks) != 0) {
638                ibex_reset(ib);
639                ret = FALSE;
640        } else {
641                ret = ib->words->klass->contains_name(ib->words, name);
642        }
643
644        ibex_unuse(ib);
645        IBEX_UNLOCK(ib);
646
647        return ret;
648}
649
Note: See TracBrowser for help on using the repository browser.