blob: e061c11b21981ba9628581fa7ff80e6b9f8e3e78 [file] [log] [blame]
Owen Taylor3473f882001-02-23 17:55:21 +00001/*
2 * hash.c: chained hash tables
3 *
4 * Reference: Your favorite introductory book on algorithms
5 *
6 * Copyright (C) 2000 Bjorn Reese and Daniel Veillard.
7 *
8 * Permission to use, copy, modify, and distribute this software for any
9 * purpose with or without fee is hereby granted, provided that the above
10 * copyright notice and this permission notice appear in all copies.
11 *
12 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
13 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
14 * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE AUTHORS AND
15 * CONTRIBUTORS ACCEPT NO RESPONSIBILITY IN ANY CONCEIVABLE MANNER.
16 *
Bjorn Reese70a9da52001-04-21 16:57:29 +000017 * Author: breese@users.sourceforge.net
Owen Taylor3473f882001-02-23 17:55:21 +000018 */
19
Bjorn Reese70a9da52001-04-21 16:57:29 +000020#include "libxml.h"
Owen Taylor3473f882001-02-23 17:55:21 +000021
22#include <string.h>
23#include <libxml/hash.h>
24#include <libxml/xmlmemory.h>
25#include <libxml/parser.h>
Daniel Veillarda10efa82001-04-18 13:09:01 +000026#include <libxml/xmlerror.h>
27
28#define MAX_HASH_LEN 8
29
30/* #define DEBUG_GROW */
Owen Taylor3473f882001-02-23 17:55:21 +000031
32/*
33 * A single entry in the hash table
34 */
35typedef struct _xmlHashEntry xmlHashEntry;
36typedef xmlHashEntry *xmlHashEntryPtr;
37struct _xmlHashEntry {
38 struct _xmlHashEntry *next;
39 xmlChar *name;
40 xmlChar *name2;
41 xmlChar *name3;
42 void *payload;
43};
44
45/*
46 * The entire hash table
47 */
48struct _xmlHashTable {
49 struct _xmlHashEntry **table;
50 int size;
51 int nbElems;
52};
53
54/*
55 * xmlHashComputeKey:
56 * Calculate the hash key
57 */
58static unsigned long
Daniel Veillarda10efa82001-04-18 13:09:01 +000059xmlHashComputeKey(xmlHashTablePtr table, const xmlChar *name,
60 const xmlChar *name2, const xmlChar *name3) {
Owen Taylor3473f882001-02-23 17:55:21 +000061 unsigned long value = 0L;
62 char ch;
63
Daniel Veillarda10efa82001-04-18 13:09:01 +000064 if (name != NULL) {
65 value += 30 * (*name);
66 while ((ch = *name++) != 0) {
67 /* value *= 31; */
68 value += (unsigned long)ch;
69 }
70 }
71 if (name2 != NULL) {
72 while ((ch = *name2++) != 0) {
73 /* value *= 31; */
74 value += (unsigned long)ch;
75 }
76 }
77 if (name3 != NULL) {
78 while ((ch = *name3++) != 0) {
79 /* value *= 31; */
80 value += (unsigned long)ch;
81 }
Owen Taylor3473f882001-02-23 17:55:21 +000082 }
83 return (value % table->size);
84}
85
86/**
87 * xmlHashCreate:
88 * @size: the size of the hash table
89 *
90 * Create a new xmlHashTablePtr.
91 *
92 * Returns the newly created object, or NULL if an error occured.
93 */
94xmlHashTablePtr
95xmlHashCreate(int size) {
96 xmlHashTablePtr table;
97
98 if (size <= 0)
99 size = 256;
100
101 table = xmlMalloc(sizeof(xmlHashTable));
102 if (table) {
103 table->size = size;
104 table->nbElems = 0;
105 table->table = xmlMalloc(size * sizeof(xmlHashEntry));
106 if (table->table) {
107 memset(table->table, 0, size * sizeof(xmlHashEntry));
108 return(table);
109 }
110 xmlFree(table);
111 }
112 return(NULL);
113}
114
115/**
Daniel Veillarda10efa82001-04-18 13:09:01 +0000116 * xmlHashGrow:
117 * @table: the hash table
118 * @size: the new size of the hash table
119 *
120 * resize the hash table
121 *
122 * Returns 0 in case of success, -1 in case of failure
123 */
Daniel Veillarddab4cb32001-04-20 13:03:48 +0000124static int
Daniel Veillarda10efa82001-04-18 13:09:01 +0000125xmlHashGrow(xmlHashTablePtr table, int size) {
126 unsigned long key;
127 int oldsize, i;
128 xmlHashEntryPtr iter, next;
129 struct _xmlHashEntry **oldtable;
130#ifdef DEBUG_GROW
131 unsigned long nbElem = 0;
132#endif
133
134 if (table == NULL)
135 return(-1);
136 if (size < 8)
137 return(-1);
138 if (size > 8 * 2048)
139 return(-1);
140
141 oldsize = table->size;
142 oldtable = table->table;
143 if (oldtable == NULL)
144 return(-1);
145
146 table->table = xmlMalloc(size * sizeof(xmlHashEntry));
147 if (table->table == NULL) {
148 table->table = oldtable;
149 return(-1);
150 }
151 memset(table->table, 0, size * sizeof(xmlHashEntry));
152 table->size = size;
153
154 for (i = 0; i < oldsize; i++) {
155 iter = oldtable[i];
156 while (iter) {
157 next = iter->next;
158
159 /*
160 * put back the entry in the new table
161 */
162
163 key = xmlHashComputeKey(table, iter->name, iter->name2,
164 iter->name3);
165 iter->next = table->table[key];
166 table->table[key] = iter;
167
168#ifdef DEBUG_GROW
169 nbElem++;
170#endif
171
172 iter = next;
173 }
174 }
175
176 xmlFree(oldtable);
177
178#ifdef DEBUG_GROW
179 xmlGenericError(xmlGenericErrorContext,
180 "xmlHashGrow : from %d to %d, %d elems\n", oldsize, size, nbElem);
181#endif
182
183 return(0);
184}
185
186/**
Owen Taylor3473f882001-02-23 17:55:21 +0000187 * xmlHashFree:
188 * @table: the hash table
189 * @f: the deallocator function for items in the hash
190 *
191 * Free the hash table and its contents. The userdata is
192 * deallocated with f if provided.
193 */
194void
195xmlHashFree(xmlHashTablePtr table, xmlHashDeallocator f) {
196 int i;
197 xmlHashEntryPtr iter;
198 xmlHashEntryPtr next;
199
200 if (table == NULL)
201 return;
202 if (table->table) {
203 for(i = 0; i < table->size; i++) {
204 iter = table->table[i];
205 while (iter) {
206 next = iter->next;
Daniel Veillarda10efa82001-04-18 13:09:01 +0000207 if (f)
208 f(iter->payload, iter->name);
Owen Taylor3473f882001-02-23 17:55:21 +0000209 if (iter->name)
210 xmlFree(iter->name);
211 if (iter->name2)
212 xmlFree(iter->name2);
213 if (iter->name3)
214 xmlFree(iter->name3);
Owen Taylor3473f882001-02-23 17:55:21 +0000215 iter->payload = NULL;
216 xmlFree(iter);
217 iter = next;
218 }
219 table->table[i] = NULL;
220 }
221 xmlFree(table->table);
222 }
223 xmlFree(table);
224}
225
226/**
227 * xmlHashAddEntry:
228 * @table: the hash table
229 * @name: the name of the userdata
230 * @userdata: a pointer to the userdata
231 *
232 * Add the userdata to the hash table. This can later be retrieved
233 * by using the name. Duplicate names generate errors.
234 *
235 * Returns 0 the addition succeeded and -1 in case of error.
236 */
237int
238xmlHashAddEntry(xmlHashTablePtr table, const xmlChar *name, void *userdata) {
239 return(xmlHashAddEntry3(table, name, NULL, NULL, userdata));
240}
241
242/**
243 * xmlHashAddEntry2:
244 * @table: the hash table
245 * @name: the name of the userdata
246 * @name2: a second name of the userdata
247 * @userdata: a pointer to the userdata
248 *
249 * Add the userdata to the hash table. This can later be retrieved
250 * by using the (name, name2) tuple. Duplicate tuples generate errors.
251 *
252 * Returns 0 the addition succeeded and -1 in case of error.
253 */
254int
255xmlHashAddEntry2(xmlHashTablePtr table, const xmlChar *name,
256 const xmlChar *name2, void *userdata) {
257 return(xmlHashAddEntry3(table, name, name2, NULL, userdata));
258}
259
260/**
261 * xmlHashUpdateEntry:
262 * @table: the hash table
263 * @name: the name of the userdata
264 * @userdata: a pointer to the userdata
265 * @f: the deallocator function for replaced item (if any)
266 *
267 * Add the userdata to the hash table. This can later be retrieved
268 * by using the name. Existing entry for this name will be removed
269 * and freed with @f if found.
270 *
271 * Returns 0 the addition succeeded and -1 in case of error.
272 */
273int
274xmlHashUpdateEntry(xmlHashTablePtr table, const xmlChar *name,
275 void *userdata, xmlHashDeallocator f) {
276 return(xmlHashUpdateEntry3(table, name, NULL, NULL, userdata, f));
277}
278
279/**
280 * xmlHashUpdateEntry2:
281 * @table: the hash table
282 * @name: the name of the userdata
283 * @name2: a second name of the userdata
284 * @userdata: a pointer to the userdata
285 * @f: the deallocator function for replaced item (if any)
286 *
287 * Add the userdata to the hash table. This can later be retrieved
288 * by using the (name, name2) tuple. Existing entry for this tuple will
289 * be removed and freed with @f if found.
290 *
291 * Returns 0 the addition succeeded and -1 in case of error.
292 */
293int
294xmlHashUpdateEntry2(xmlHashTablePtr table, const xmlChar *name,
295 const xmlChar *name2, void *userdata,
296 xmlHashDeallocator f) {
297 return(xmlHashUpdateEntry3(table, name, name2, NULL, userdata, f));
298}
299
300/**
301 * xmlHashLookup:
302 * @table: the hash table
303 * @name: the name of the userdata
304 *
305 * Find the userdata specified by the name.
306 *
307 * Returns the a pointer to the userdata
308 */
309void *
310xmlHashLookup(xmlHashTablePtr table, const xmlChar *name) {
311 return(xmlHashLookup3(table, name, NULL, NULL));
312}
313
314/**
315 * xmlHashLookup2:
316 * @table: the hash table
317 * @name: the name of the userdata
318 * @name2: a second name of the userdata
319 *
320 * Find the userdata specified by the (name, name2) tuple.
321 *
322 * Returns the a pointer to the userdata
323 */
324void *
325xmlHashLookup2(xmlHashTablePtr table, const xmlChar *name,
326 const xmlChar *name2) {
327 return(xmlHashLookup3(table, name, name2, NULL));
328}
329
330/**
331 * xmlHashAddEntry3:
332 * @table: the hash table
333 * @name: the name of the userdata
334 * @name2: a second name of the userdata
335 * @name3: a third name of the userdata
336 * @userdata: a pointer to the userdata
337 *
338 * Add the userdata to the hash table. This can later be retrieved
339 * by using the tuple (name, name2, name3). Duplicate entries generate
340 * errors.
341 *
342 * Returns 0 the addition succeeded and -1 in case of error.
343 */
344int
345xmlHashAddEntry3(xmlHashTablePtr table, const xmlChar *name,
346 const xmlChar *name2, const xmlChar *name3,
347 void *userdata) {
Daniel Veillarda10efa82001-04-18 13:09:01 +0000348 unsigned long key, len = 0;
Owen Taylor3473f882001-02-23 17:55:21 +0000349 xmlHashEntryPtr entry;
350 xmlHashEntryPtr insert;
351
352 if ((table == NULL) || name == NULL)
353 return(-1);
354
355 /*
356 * Check for duplicate and insertion location.
357 */
Daniel Veillarda10efa82001-04-18 13:09:01 +0000358 key = xmlHashComputeKey(table, name, name2, name3);
Owen Taylor3473f882001-02-23 17:55:21 +0000359 if (table->table[key] == NULL) {
360 insert = NULL;
361 } else {
362 for (insert = table->table[key]; insert->next != NULL;
363 insert = insert->next) {
364 if ((xmlStrEqual(insert->name, name)) &&
365 (xmlStrEqual(insert->name2, name2)) &&
366 (xmlStrEqual(insert->name3, name3)))
367 return(-1);
Daniel Veillarda10efa82001-04-18 13:09:01 +0000368 len++;
Owen Taylor3473f882001-02-23 17:55:21 +0000369 }
370 if ((xmlStrEqual(insert->name, name)) &&
371 (xmlStrEqual(insert->name2, name2)) &&
372 (xmlStrEqual(insert->name3, name3)))
373 return(-1);
374 }
375
376 entry = xmlMalloc(sizeof(xmlHashEntry));
377 if (entry == NULL)
378 return(-1);
379 entry->name = xmlStrdup(name);
380 entry->name2 = xmlStrdup(name2);
381 entry->name3 = xmlStrdup(name3);
382 entry->payload = userdata;
383 entry->next = NULL;
384
385
386 if (insert == NULL) {
387 table->table[key] = entry;
388 } else {
389 insert->next = entry;
390 }
391 table->nbElems++;
Daniel Veillarda10efa82001-04-18 13:09:01 +0000392
393 if (len > MAX_HASH_LEN)
394 xmlHashGrow(table, MAX_HASH_LEN * table->size);
395
Owen Taylor3473f882001-02-23 17:55:21 +0000396 return(0);
397}
398
399/**
400 * xmlHashUpdateEntry3:
401 * @table: the hash table
402 * @name: the name of the userdata
403 * @name2: a second name of the userdata
404 * @name3: a third name of the userdata
405 * @userdata: a pointer to the userdata
406 * @f: the deallocator function for replaced item (if any)
407 *
408 * Add the userdata to the hash table. This can later be retrieved
409 * by using the tuple (name, name2, name3). Existing entry for this tuple
410 * will be removed and freed with @f if found.
411 *
412 * Returns 0 the addition succeeded and -1 in case of error.
413 */
414int
415xmlHashUpdateEntry3(xmlHashTablePtr table, const xmlChar *name,
416 const xmlChar *name2, const xmlChar *name3,
417 void *userdata, xmlHashDeallocator f) {
418 unsigned long key;
419 xmlHashEntryPtr entry;
420 xmlHashEntryPtr insert;
421
422 if ((table == NULL) || name == NULL)
423 return(-1);
424
425 /*
426 * Check for duplicate and insertion location.
427 */
Daniel Veillarda10efa82001-04-18 13:09:01 +0000428 key = xmlHashComputeKey(table, name, name2, name3);
Owen Taylor3473f882001-02-23 17:55:21 +0000429 if (table->table[key] == NULL) {
430 insert = NULL;
431 } else {
432 for (insert = table->table[key]; insert->next != NULL;
433 insert = insert->next) {
434 if ((xmlStrEqual(insert->name, name)) &&
435 (xmlStrEqual(insert->name2, name2)) &&
436 (xmlStrEqual(insert->name3, name3))) {
437 if (f)
438 f(insert->payload, insert->name);
439 insert->payload = userdata;
440 return(0);
441 }
442 }
443 if ((xmlStrEqual(insert->name, name)) &&
444 (xmlStrEqual(insert->name2, name2)) &&
445 (xmlStrEqual(insert->name3, name3))) {
446 if (f)
447 f(insert->payload, insert->name);
448 insert->payload = userdata;
449 return(0);
450 }
451 }
452
453 entry = xmlMalloc(sizeof(xmlHashEntry));
454 if (entry == NULL)
455 return(-1);
456 entry->name = xmlStrdup(name);
457 entry->name2 = xmlStrdup(name2);
458 entry->name3 = xmlStrdup(name3);
459 entry->payload = userdata;
460 entry->next = NULL;
461 table->nbElems++;
462
463
464 if (insert == NULL) {
465 table->table[key] = entry;
466 } else {
467 insert->next = entry;
468 }
469 return(0);
470}
471
472/**
473 * xmlHashLookup:
474 * @table: the hash table
475 * @name: the name of the userdata
476 * @name2: a second name of the userdata
477 * @name3: a third name of the userdata
478 *
479 * Find the userdata specified by the (name, name2, name3) tuple.
480 *
481 * Returns the a pointer to the userdata
482 */
483void *
484xmlHashLookup3(xmlHashTablePtr table, const xmlChar *name,
485 const xmlChar *name2, const xmlChar *name3) {
486 unsigned long key;
487 xmlHashEntryPtr entry;
488
489 if (table == NULL)
490 return(NULL);
491 if (name == NULL)
492 return(NULL);
Daniel Veillarda10efa82001-04-18 13:09:01 +0000493 key = xmlHashComputeKey(table, name, name2, name3);
Owen Taylor3473f882001-02-23 17:55:21 +0000494 for (entry = table->table[key]; entry != NULL; entry = entry->next) {
495 if ((xmlStrEqual(entry->name, name)) &&
496 (xmlStrEqual(entry->name2, name2)) &&
497 (xmlStrEqual(entry->name3, name3)))
498 return(entry->payload);
499 }
500 return(NULL);
501}
502
503/**
504 * xmlHashScan:
505 * @table: the hash table
506 * @f: the scanner function for items in the hash
507 * @data: extra data passed to f
508 *
509 * Scan the hash table and applied f to each value.
510 */
511void
512xmlHashScan(xmlHashTablePtr table, xmlHashScanner f, void *data) {
513 int i;
514 xmlHashEntryPtr iter;
515 xmlHashEntryPtr next;
516
517 if (table == NULL)
518 return;
519 if (f == NULL)
520 return;
521
522 if (table->table) {
523 for(i = 0; i < table->size; i++) {
524 iter = table->table[i];
525 while (iter) {
526 next = iter->next;
527 if (f)
528 f(iter->payload, data, iter->name);
529 iter = next;
530 }
531 }
532 }
533}
534
535/**
536 * xmlHashScan3:
537 * @table: the hash table
538 * @name: the name of the userdata or NULL
539 * @name2: a second name of the userdata or NULL
540 * @name3: a third name of the userdata or NULL
541 * @f: the scanner function for items in the hash
542 * @data: extra data passed to f
543 *
544 * Scan the hash table and applied f to each value matching
545 * (name, name2, name3) tuple. If one of the names is null,
546 * the comparison is considered to match.
547 */
548void
549xmlHashScan3(xmlHashTablePtr table, const xmlChar *name,
550 const xmlChar *name2, const xmlChar *name3,
551 xmlHashScanner f, void *data) {
552 int i;
553 xmlHashEntryPtr iter;
554 xmlHashEntryPtr next;
555
556 if (table == NULL)
557 return;
558 if (f == NULL)
559 return;
560
561 if (table->table) {
562 for(i = 0; i < table->size; i++) {
563 iter = table->table[i];
564 while (iter) {
565 next = iter->next;
566 if (((name == NULL) || (xmlStrEqual(name, iter->name))) &&
567 ((name2 == NULL) || (xmlStrEqual(name2, iter->name2))) &&
568 ((name3 == NULL) || (xmlStrEqual(name3, iter->name3)))) {
569 f(iter->payload, data, iter->name);
570 }
571 iter = next;
572 }
573 }
574 }
575}
576
577/**
578 * xmlHashCopy:
579 * @table: the hash table
580 * @f: the copier function for items in the hash
581 *
582 * Scan the hash table and applied f to each value.
583 *
584 * Returns the new table or NULL in case of error.
585 */
586xmlHashTablePtr
587xmlHashCopy(xmlHashTablePtr table, xmlHashCopier f) {
588 int i;
589 xmlHashEntryPtr iter;
590 xmlHashEntryPtr next;
591 xmlHashTablePtr ret;
592
593 if (table == NULL)
594 return(NULL);
595 if (f == NULL)
596 return(NULL);
597
598 ret = xmlHashCreate(table->size);
599 if (table->table) {
600 for(i = 0; i < table->size; i++) {
601 iter = table->table[i];
602 while (iter) {
603 next = iter->next;
604 xmlHashAddEntry3(ret, iter->name, iter->name2,
605 iter->name3, f(iter->payload, iter->name));
606 iter = next;
607 }
608 }
609 }
610 ret->nbElems = table->nbElems;
611 return(ret);
612}
613
614/**
615 * xmlHashSize:
616 * @table: the hash table
617 *
618 * Returns the number of elements in the hash table or
619 * -1 in case of error
620 */
621int
622xmlHashSize(xmlHashTablePtr table) {
623 if (table == NULL)
624 return(-1);
625 return(table->nbElems);
626}
627
628/**
Daniel Veillardf69bb4b2001-05-19 13:24:56 +0000629 * xmlHashRemoveEntry:
Owen Taylor3473f882001-02-23 17:55:21 +0000630 * @table: the hash table
631 * @name: the name of the userdata
632 * @f: the deallocator function for removed item (if any)
633 *
634 * Find the userdata specified by the (name, name2, name3) tuple and remove
635 * it from the hash table. Existing userdata for this tuple will be removed
636 * and freed with @f.
637 *
638 * Returns 0 if the removal succeeded and -1 in case of error or not found.
639 */
640int xmlHashRemoveEntry(xmlHashTablePtr table, const xmlChar *name,
Daniel Veillarda10efa82001-04-18 13:09:01 +0000641 xmlHashDeallocator f) {
642 return(xmlHashRemoveEntry3(table, name, NULL, NULL, f));
Owen Taylor3473f882001-02-23 17:55:21 +0000643}
644
645/**
Daniel Veillardf69bb4b2001-05-19 13:24:56 +0000646 * xmlHashRemoveEntry2:
Owen Taylor3473f882001-02-23 17:55:21 +0000647 * @table: the hash table
648 * @name: the name of the userdata
649 * @name2: a second name of the userdata
650 * @f: the deallocator function for removed item (if any)
651 *
652 * Find the userdata specified by the (name, name2, name3) tuple and remove
653 * it from the hash table. Existing userdata for this tuple will be removed
654 * and freed with @f.
655 *
656 * Returns 0 if the removal succeeded and -1 in case of error or not found.
657 */
658int xmlHashRemoveEntry2(xmlHashTablePtr table, const xmlChar *name,
Daniel Veillarda10efa82001-04-18 13:09:01 +0000659 const xmlChar *name2, xmlHashDeallocator f) {
660 return(xmlHashRemoveEntry3(table, name, name2, NULL, f));
Owen Taylor3473f882001-02-23 17:55:21 +0000661}
662
663/**
Daniel Veillardf69bb4b2001-05-19 13:24:56 +0000664 * xmlHashRemoveEntry3
Owen Taylor3473f882001-02-23 17:55:21 +0000665 * @table: the hash table
666 * @name: the name of the userdata
667 * @name2: a second name of the userdata
668 * @name3: a third name of the userdata
669 * @f: the deallocator function for removed item (if any)
670 *
671 * Find the userdata specified by the (name, name2, name3) tuple and remove
672 * it from the hash table. Existing userdata for this tuple will be removed
673 * and freed with @f.
674 *
675 * Returns 0 if the removal succeeded and -1 in case of error or not found.
676 */
677int xmlHashRemoveEntry3(xmlHashTablePtr table, const xmlChar *name,
Daniel Veillarda10efa82001-04-18 13:09:01 +0000678 const xmlChar *name2, const xmlChar *name3, xmlHashDeallocator f) {
679 unsigned long key;
680 xmlHashEntryPtr entry;
681 xmlHashEntryPtr prev = NULL;
Owen Taylor3473f882001-02-23 17:55:21 +0000682
Daniel Veillarda10efa82001-04-18 13:09:01 +0000683 if (table == NULL || name == NULL)
684 return(-1);
Owen Taylor3473f882001-02-23 17:55:21 +0000685
Daniel Veillarda10efa82001-04-18 13:09:01 +0000686 key = xmlHashComputeKey(table, name, name2, name3);
687 if (table->table[key] == NULL) {
688 return(-1);
689 } else {
690 for (entry = table->table[key]; entry != NULL; entry = entry->next) {
691 if (xmlStrEqual(entry->name, name) &&
692 xmlStrEqual(entry->name2, name2) &&
693 xmlStrEqual(entry->name3, name3)) {
694 if(f)
695 f(entry->payload, entry->name);
696 entry->payload = NULL;
697 if(entry->name)
698 xmlFree(entry->name);
699 if(entry->name2)
700 xmlFree(entry->name2);
701 if(entry->name3)
702 xmlFree(entry->name3);
703 if(prev)
704 prev->next = entry->next;
705 else
706 table->table[key] = entry->next;
707 xmlFree(entry);
708 table->nbElems--;
709 return(0);
710 }
711 prev = entry;
712 }
713 return(-1);
714 }
Daniel Veillard9e7160d2001-03-18 23:17:47 +0000715}
716