POSIX thread updates. First work on term sets.
[idzebra-moved-to-github.git] / index / zsets.c
1 /*
2  * Copyright (C) 1994-2000, Index Data
3  * All rights reserved.
4  * Sebastian Hammer, Adam Dickmeiss
5  *
6  * $Log: zsets.c,v $
7  * Revision 1.30  2001-10-15 19:53:43  adam
8  * POSIX thread updates. First work on term sets.
9  *
10  * Revision 1.29  2001/01/22 10:42:56  adam
11  * Added numerical sort.
12  *
13  * Revision 1.28  2000/07/07 12:49:20  adam
14  * Optimized resultSetInsert{Rank,Sort}.
15  *
16  * Revision 1.27  2000/04/05 09:49:36  adam
17  * On Unix, zebra/z'mbol uses automake.
18  *
19  * Revision 1.26  2000/03/20 19:08:36  adam
20  * Added remote record import using Z39.50 extended services and Segment
21  * Requests.
22  *
23  * Revision 1.25  2000/03/15 15:00:31  adam
24  * First work on threaded version.
25  *
26  * Revision 1.24  1999/11/04 15:00:45  adam
27  * Implemented delete result set(s).
28  *
29  * Revision 1.23  1999/05/26 07:49:13  adam
30  * C++ compilation.
31  *
32  * Revision 1.22  1999/02/02 14:51:15  adam
33  * Updated WIN32 code specific sections. Changed header.
34  *
35  * Revision 1.21  1998/11/16 16:03:46  adam
36  * Moved loggin utilities to Yaz. Was implemented in file zlogs.c.
37  *
38  * Revision 1.20  1998/11/16 10:10:53  adam
39  * Fixed problem with zebraPosSetCreate that occurred when positions were
40  * less than 1.
41  *
42  * Revision 1.19  1998/09/22 10:48:22  adam
43  * Minor changes in search API.
44  *
45  * Revision 1.18  1998/09/22 10:03:45  adam
46  * Changed result sets to be persistent in the sense that they can
47  * be re-searched if needed.
48  * Fixed memory leak in rsm_or.
49  *
50  * Revision 1.17  1998/06/23 15:33:36  adam
51  * Added feature to specify sort criteria in query (type 7 specifies
52  * sort flags).
53  *
54  * Revision 1.16  1998/05/20 10:12:24  adam
55  * Implemented automatic EXPLAIN database maintenance.
56  * Modified Zebra to work with ASN.1 compiled version of YAZ.
57  *
58  * Revision 1.15  1998/03/05 08:45:14  adam
59  * New result set model and modular ranking system. Moved towards
60  * descent server API. System information stored as "SGML" records.
61  *
62  * Revision 1.14  1998/02/10 16:39:15  adam
63  * Minor change.
64  *
65  * Revision 1.13  1998/02/10 12:03:06  adam
66  * Implemented Sort.
67  *
68  * Revision 1.12  1997/09/25 14:57:36  adam
69  * Windows NT port.
70  *
71  * Revision 1.11  1996/12/23 15:30:46  adam
72  * Work on truncation.
73  * Bug fix: result sets weren't deleted after server shut down.
74  *
75  * Revision 1.10  1995/10/30 15:08:08  adam
76  * Bug fixes.
77  *
78  * Revision 1.9  1995/10/17  18:02:14  adam
79  * New feature: databases. Implemented as prefix to words in dictionary.
80  *
81  * Revision 1.8  1995/10/10  13:59:25  adam
82  * Function rset_open changed its wflag parameter to general flags.
83  *
84  * Revision 1.7  1995/10/06  14:38:01  adam
85  * New result set method: r_score.
86  * Local no (sysno) and score is transferred to retrieveCtrl.
87  *
88  * Revision 1.6  1995/09/28  09:19:49  adam
89  * xfree/xmalloc used everywhere.
90  * Extract/retrieve method seems to work for text records.
91  *
92  * Revision 1.5  1995/09/27  16:17:32  adam
93  * More work on retrieve.
94  *
95  * Revision 1.4  1995/09/07  13:58:36  adam
96  * New parameter: result-set file descriptor (RSFD) to support multiple
97  * positions within the same result-set.
98  * Boolean operators: and, or, not implemented.
99  * Result-set references.
100  *
101  * Revision 1.3  1995/09/06  16:11:19  adam
102  * Option: only one word key per file.
103  *
104  * Revision 1.2  1995/09/06  10:33:04  adam
105  * More work on present. Some log messages removed.
106  *
107  * Revision 1.1  1995/09/05  15:28:40  adam
108  * More work on search engine.
109  *
110  */
111 #include <stdio.h>
112 #include <assert.h>
113 #ifdef WIN32
114 #include <io.h>
115 #else
116 #include <unistd.h>
117 #endif
118
119 #include "zserver.h"
120 #include <rstemp.h>
121
122 #define SORT_IDX_ENTRYSIZE 64
123 #define ZSET_SORT_MAX_LEVEL 3
124
125 struct zebra_set_term_entry {
126     int reg_type;
127     char *db;
128     int set;
129     int use;
130     char *term;
131 };
132 struct zebra_set {
133     char *name;
134     RSET rset;
135     NMEM nmem;
136     int hits;
137     int num_bases;
138     char **basenames;
139     Z_RPNQuery *rpn;
140     struct zset_sort_info *sort_info;
141     struct zebra_set_term_entry *term_entries;
142     int term_entries_max;
143     struct zebra_set *next;
144     int locked;
145 };
146
147 struct zset_sort_entry {
148     int sysno;
149     int score;
150     char buf[ZSET_SORT_MAX_LEVEL][SORT_IDX_ENTRYSIZE];
151 };
152
153 struct zset_sort_info {
154     int max_entries;
155     int num_entries;
156     struct zset_sort_entry *all_entries;
157     struct zset_sort_entry **entries;
158 };
159
160 ZebraSet resultSetAddRPN (ZebraHandle zh, ODR input, ODR output,
161                           Z_RPNQuery *rpn, int num_bases, char **basenames, 
162                           const char *setname)
163 {
164     ZebraSet zebraSet;
165
166     zh->errCode = 0;
167     zh->errString = NULL;
168     zh->hits = 0;
169
170     zebraSet = resultSetAdd (zh, setname, 1);
171     if (!zebraSet)
172         return 0;
173     zebraSet->locked = 1;
174     zebraSet->rpn = 0;
175     zebraSet->num_bases = num_bases;
176     zebraSet->basenames = basenames;
177     zebraSet->nmem = odr_extract_mem (input);
178
179     zebraSet->rset = rpn_search (zh, output->mem, rpn,
180                                  zebraSet->num_bases,
181                                  zebraSet->basenames, zebraSet->name,
182                                  zebraSet);
183     zh->hits = zebraSet->hits;
184     if (zebraSet->rset)
185         zebraSet->rpn = rpn;
186     zebraSet->locked = 0;
187     return zebraSet;
188 }
189
190 void resultSetAddTerm (ZebraHandle zh, ZebraSet s, int reg_type,
191                        const char *db, int set,
192                        int use, const char *term)
193 {
194     if (!s->nmem)
195         s->nmem = nmem_create ();
196     if (!s->term_entries)
197     {
198         int i;
199         s->term_entries_max = 1000;
200         s->term_entries =
201             nmem_malloc (s->nmem, s->term_entries_max * 
202                          sizeof(*s->term_entries));
203         for (i = 0; i < s->term_entries_max; i++)
204             s->term_entries[i].term = 0;
205     }
206     if (s->hits < s->term_entries_max)
207     {
208         s->term_entries[s->hits].reg_type = reg_type;
209         s->term_entries[s->hits].db = nmem_strdup (s->nmem, db);
210         s->term_entries[s->hits].set = set;
211         s->term_entries[s->hits].use = use;
212         s->term_entries[s->hits].term = nmem_strdup (s->nmem, term);
213     }
214     (s->hits)++;
215 }
216
217 ZebraSet resultSetAdd (ZebraHandle zh, const char *name, int ov)
218 {
219     ZebraSet s;
220     int i;
221
222     for (s = zh->sets; s; s = s->next)
223         if (!strcmp (s->name, name))
224             break;
225     if (s)
226     {
227         logf (LOG_DEBUG, "updating result set %s", name);
228         if (!ov || s->locked)
229             return NULL;
230         if (s->rset)
231             rset_delete (s->rset);
232         if (s->nmem)
233             nmem_destroy (s->nmem);
234     }
235     else
236     {
237         logf (LOG_DEBUG, "adding result set %s", name);
238         s = (ZebraSet) xmalloc (sizeof(*s));
239         s->next = zh->sets;
240         zh->sets = s;
241         s->name = (char *) xmalloc (strlen(name)+1);
242         strcpy (s->name, name);
243
244         s->sort_info = (struct zset_sort_info *)
245             xmalloc (sizeof(*s->sort_info));
246         s->sort_info->max_entries = 1000;
247         s->sort_info->entries = (struct zset_sort_entry **)
248             xmalloc (sizeof(*s->sort_info->entries) *
249                      s->sort_info->max_entries);
250         s->sort_info->all_entries = (struct zset_sort_entry *)
251             xmalloc (sizeof(*s->sort_info->all_entries) *
252                      s->sort_info->max_entries);
253         for (i = 0; i < s->sort_info->max_entries; i++)
254             s->sort_info->entries[i] = s->sort_info->all_entries + i;
255     }
256     s->locked = 0;
257     s->term_entries = 0;
258     s->hits = 0;
259     s->rset = 0;
260     s->nmem = 0;
261     s->rpn = 0;
262     return s;
263 }
264
265 ZebraSet resultSetGet (ZebraHandle zh, const char *name)
266 {
267     ZebraSet s;
268
269     for (s = zh->sets; s; s = s->next)
270         if (!strcmp (s->name, name))
271         {
272             if (!s->term_entries && !s->rset && s->rpn)
273             {
274                 NMEM nmem = nmem_create ();
275                 s->rset =
276                     rpn_search (zh, nmem, s->rpn, s->num_bases,
277                                 s->basenames, s->name, s);
278                 nmem_destroy (nmem);
279             }
280             return s;
281         }
282     return NULL;
283 }
284
285 void resultSetDestroy (ZebraHandle zh, int num, char **names,int *statuses)
286 {
287     ZebraSet *ss = &zh->sets;
288     int i;
289     
290     if (statuses)
291         for (i = 0; i<num; i++)
292             statuses[i] = Z_DeleteStatus_resultSetDidNotExist;
293     zh->errCode = 0;
294     zh->errString = NULL;
295     while (*ss)
296     {
297         int i = -1;
298         ZebraSet s = *ss;
299         if (num >= 0)
300         {
301             for (i = 0; i<num; i++)
302                 if (!strcmp (s->name, names[i]))
303                 {
304                     if (statuses)
305                         statuses[i] = Z_DeleteStatus_success;
306                     i = -1;
307                     break;
308                 }
309         }
310         if (i < 0)
311         {
312             *ss = s->next;
313             
314             xfree (s->sort_info->all_entries);
315             xfree (s->sort_info->entries);
316             xfree (s->sort_info);
317             
318             if (s->nmem)
319                 nmem_destroy (s->nmem);
320             if (s->rset)
321                 rset_delete (s->rset);
322             xfree (s->name);
323             xfree (s);
324         }
325         else
326             ss = &s->next;
327     }
328 }
329
330 ZebraPosSet zebraPosSetCreate (ZebraHandle zh, const char *name, 
331                                int num, int *positions)
332 {
333     ZebraSet sset;
334     ZebraPosSet sr = 0;
335     RSET rset;
336     int i;
337     struct zset_sort_info *sort_info;
338
339     if (!(sset = resultSetGet (zh, name)))
340         return NULL;
341     if (!(rset = sset->rset))
342     {
343         if (!sset->term_entries)
344             return 0;
345         sr = (ZebraPosSet) xmalloc (sizeof(*sr) * num);
346         for (i = 0; i<num; i++)
347         {
348             int j;
349             struct zebra_set_term_entry *entry = sset->term_entries;
350
351             sr[i].sysno = 0;
352             sr[i].score = -1;
353             sr[i].term = 0;
354             sr[i].db = 0;
355
356             if (positions[i] <= sset->term_entries_max)
357             {
358                 sr[i].term = sset->term_entries[positions[i]-1].term;
359                 sr[i].db = sset->term_entries[positions[i]-1].db;
360             }
361         }
362     }
363     else
364     {
365         sr = (ZebraPosSet) xmalloc (sizeof(*sr) * num);
366         for (i = 0; i<num; i++)
367         {
368             sr[i].sysno = 0;
369             sr[i].score = -1;
370             sr[i].term = 0;
371             sr[i].db = 0;
372         }
373         sort_info = sset->sort_info;
374         if (sort_info)
375         {
376             int position;
377             
378             for (i = 0; i<num; i++)
379             {
380                 position = positions[i];
381                 if (position > 0 && position <= sort_info->num_entries)
382                 {
383                     logf (LOG_DEBUG, "got pos=%d (sorted)", position);
384                     sr[i].sysno = sort_info->entries[position-1]->sysno;
385                     sr[i].score = sort_info->entries[position-1]->score;
386                 }
387             }
388         }
389         /* did we really get all entries using sort ? */
390         for (i = 0; i<num; i++)
391         {
392             if (!sr[i].sysno)
393                 break;
394         }
395         if (i < num) /* nope, get the rest, unsorted - sorry */
396         {
397             int position = 0;
398             int num_i = 0;
399             int psysno = 0;
400             int term_index;
401             RSFD rfd;
402             struct it_key key;
403             
404             if (sort_info)
405                 position = sort_info->num_entries;
406             while (num_i < num && positions[num_i] < position)
407                 num_i++;
408             rfd = rset_open (rset, RSETF_READ);
409             while (num_i < num && rset_read (rset, rfd, &key, &term_index))
410             {
411                 if (key.sysno != psysno)
412                 {
413                     psysno = key.sysno;
414                     if (sort_info)
415                     {
416                         /* determine we alreay have this in our set */
417                         for (i = sort_info->num_entries; --i >= 0; )
418                             if (psysno == sort_info->entries[i]->sysno)
419                                 break;
420                         if (i >= 0)
421                             continue;
422                     }
423                     position++;
424                     assert (num_i < num);
425                     if (position == positions[num_i])
426                     {
427                         sr[num_i].sysno = psysno;
428                         logf (LOG_DEBUG, "got pos=%d (unsorted)", position);
429                         sr[num_i].score = -1;
430                         num_i++;
431                     }
432                 }
433             }
434             rset_close (rset, rfd);
435         }
436     }
437     return sr;
438 }
439
440 void zebraPosSetDestroy (ZebraHandle zh, ZebraPosSet records, int num)
441 {
442     xfree (records);
443 }
444
445 struct sortKeyInfo {
446     int relation;
447     int attrUse;
448     int numerical;
449 };
450
451 void resultSetInsertSort (ZebraHandle zh, ZebraSet sset,
452                           struct sortKeyInfo *criteria, int num_criteria,
453                           int sysno)
454 {
455     struct zset_sort_entry this_entry;
456     struct zset_sort_entry *new_entry = NULL;
457     struct zset_sort_info *sort_info = sset->sort_info;
458     int i, j;
459
460     sortIdx_sysno (zh->service->sortIdx, sysno);
461     for (i = 0; i<num_criteria; i++)
462     {
463         sortIdx_type (zh->service->sortIdx, criteria[i].attrUse);
464         sortIdx_read (zh->service->sortIdx, this_entry.buf[i]);
465     }
466     i = sort_info->num_entries;
467     while (--i >= 0)
468     {
469         int rel = 0;
470         for (j = 0; j<num_criteria; j++)
471         {
472             if (criteria[j].numerical)
473             {
474                 double diff = atof(this_entry.buf[j]) -
475                               atof(sort_info->entries[i]->buf[j]);
476                 rel = 0;
477                 if (diff > 0.0)
478                     rel = 1;
479                 else if (diff < 0.0)
480                     rel = -1;
481             }
482             else
483             {
484                 rel = memcmp (this_entry.buf[j], sort_info->entries[i]->buf[j],
485                           SORT_IDX_ENTRYSIZE);
486             }
487             if (rel)
488                 break;
489         }       
490         if (!rel)
491             break;
492         if (criteria[j].relation == 'A')
493         {
494             if (rel > 0)
495                 break;
496         }
497         else if (criteria[j].relation == 'D')
498         {
499             if (rel < 0)
500                 break;
501         }
502     }
503     ++i;
504     j = sort_info->max_entries;
505     if (i == j)
506         return;
507
508     if (sort_info->num_entries == j)
509         --j;
510     else
511         j = (sort_info->num_entries)++;
512     new_entry = sort_info->entries[j];
513     while (j != i)
514     {
515         sort_info->entries[j] = sort_info->entries[j-1];
516         --j;
517     }
518     sort_info->entries[i] = new_entry;
519     assert (new_entry);
520     for (i = 0; i<num_criteria; i++)
521         memcpy (new_entry->buf[i], this_entry.buf[i], SORT_IDX_ENTRYSIZE);
522     new_entry->sysno = sysno;
523     new_entry->score = -1;
524 }
525
526 void resultSetInsertRank (ZebraHandle zh, struct zset_sort_info *sort_info,
527                           int sysno, int score, int relation)
528 {
529     struct zset_sort_entry *new_entry = NULL;
530     int i, j;
531
532     i = sort_info->num_entries;
533     while (--i >= 0)
534     {
535         int rel = 0;
536
537         rel = score - sort_info->entries[i]->score;
538
539         if (relation == 'D')
540         {
541             if (rel >= 0)
542                 break;
543         }
544         else if (relation == 'A')
545         {
546             if (rel <= 0)
547                 break;
548         }
549     }
550     ++i;
551     j = sort_info->max_entries;
552     if (i == j)
553         return;
554
555     if (sort_info->num_entries == j)
556         --j;
557     else
558         j = (sort_info->num_entries)++;
559     
560     new_entry = sort_info->entries[j];
561     while (j != i)
562     {
563         sort_info->entries[j] = sort_info->entries[j-1];
564         --j;
565     }
566     sort_info->entries[i] = new_entry;
567     assert (new_entry);
568     new_entry->sysno = sysno;
569     new_entry->score = score;
570 }
571
572 void resultSetSort (ZebraHandle zh, NMEM nmem,
573                     int num_input_setnames, const char **input_setnames,
574                     const char *output_setname,
575                     Z_SortKeySpecList *sort_sequence, int *sort_status)
576 {
577     ZebraSet sset;
578     RSET rset;
579
580     if (num_input_setnames == 0)
581     {
582         zh->errCode = 208;
583         return ;
584     }
585     if (num_input_setnames > 1)
586     {
587         zh->errCode = 230;
588         return;
589     }
590     logf (LOG_DEBUG, "result set sort input=%s output=%s",
591           *input_setnames, output_setname);
592     sset = resultSetGet (zh, input_setnames[0]);
593     if (!sset)
594     {
595         zh->errCode = 30;
596         zh->errString = nmem_strdup (nmem, input_setnames[0]);
597         return;
598     }
599     if (!(rset = sset->rset))
600     {
601         zh->errCode = 30;
602         zh->errString = nmem_strdup (nmem, input_setnames[0]);
603         return;
604     }
605     if (strcmp (output_setname, input_setnames[0]))
606     {
607         rset = rset_dup (rset);
608         sset = resultSetAdd (zh, output_setname, 1);
609         sset->rset = rset;
610     }
611     resultSetSortSingle (zh, nmem, sset, rset, sort_sequence, sort_status);
612 }
613
614 void resultSetSortSingle (ZebraHandle zh, NMEM nmem,
615                           ZebraSet sset, RSET rset,
616                           Z_SortKeySpecList *sort_sequence, int *sort_status)
617 {
618     int i, psysno = 0;
619     struct it_key key;
620     struct sortKeyInfo sort_criteria[3];
621     int num_criteria;
622     int term_index;
623     RSFD rfd;
624
625     logf (LOG_DEBUG, "resultSetSortSingle start");
626     sset->sort_info->num_entries = 0;
627
628     sset->hits = 0;
629     num_criteria = sort_sequence->num_specs;
630     if (num_criteria > 3)
631         num_criteria = 3;
632     for (i = 0; i < num_criteria; i++)
633     {
634         Z_SortKeySpec *sks = sort_sequence->specs[i];
635         Z_SortKey *sk;
636
637         if (*sks->sortRelation == Z_SortRelation_ascending)
638             sort_criteria[i].relation = 'A';
639         else if (*sks->sortRelation == Z_SortRelation_descending)
640             sort_criteria[i].relation = 'D';
641         else
642         {
643             zh->errCode = 214;
644             return;
645         }
646         if (sks->sortElement->which == Z_SortElement_databaseSpecific)
647         {
648             zh->errCode = 210;
649             return;
650         }
651         else if (sks->sortElement->which != Z_SortElement_generic)
652         {
653             zh->errCode = 237;
654             return;
655         }       
656         sk = sks->sortElement->u.generic;
657         switch (sk->which)
658         {
659         case Z_SortKey_sortField:
660             logf (LOG_DEBUG, "Sort: key %d is of type sortField", i+1);
661             zh->errCode = 207;
662             return;
663         case Z_SortKey_elementSpec:
664             logf (LOG_DEBUG, "Sort: key %d is of type elementSpec", i+1);
665             zh->errCode = 207;
666             return;
667         case Z_SortKey_sortAttributes:
668             logf (LOG_DEBUG, "Sort: key %d is of type sortAttributes", i+1);
669             sort_criteria[i].attrUse =
670                 zebra_maps_sort (zh->service->zebra_maps,
671                                  sk->u.sortAttributes,
672                                  &sort_criteria[i].numerical);
673             logf (LOG_DEBUG, "use value = %d", sort_criteria[i].attrUse);
674             if (sort_criteria[i].attrUse == -1)
675             {
676                 zh->errCode = 116;
677                 return;
678             }
679             if (sortIdx_type (zh->service->sortIdx, sort_criteria[i].attrUse))
680             {
681                 zh->errCode = 207;
682                 return;
683             }
684             break;
685         }
686     }
687     rfd = rset_open (rset, RSETF_READ);
688     while (rset_read (rset, rfd, &key, &term_index))
689     {
690         if (key.sysno != psysno)
691         {
692             (sset->hits)++;
693             psysno = key.sysno;
694             resultSetInsertSort (zh, sset,
695                                  sort_criteria, num_criteria, psysno);
696         }
697     }
698     rset_close (rset, rfd);
699
700     *sort_status = Z_SortStatus_success;
701     logf (LOG_DEBUG, "resultSetSortSingle end");
702 }
703
704 RSET resultSetRef (ZebraHandle zh, Z_ResultSetId *resultSetId)
705 {
706     ZebraSet s;
707
708     if ((s = resultSetGet (zh, resultSetId)))
709         return s->rset;
710     return NULL;
711 }
712
713 void resultSetRank (ZebraHandle zh, ZebraSet zebraSet, RSET rset)
714 {
715     int kno = 0;
716     struct it_key key;
717     RSFD rfd;
718     int term_index, i;
719     ZebraRankClass rank_class;
720     struct rank_control *rc;
721     struct zset_sort_info *sort_info;
722
723     sort_info = zebraSet->sort_info;
724     sort_info->num_entries = 0;
725     zebraSet->hits = 0;
726     rfd = rset_open (rset, RSETF_READ);
727
728     logf (LOG_DEBUG, "resultSetRank");
729     for (i = 0; i < rset->no_rset_terms; i++)
730         logf (LOG_DEBUG, "term=\"%s\" cnt=%d type=%s",
731               rset->rset_terms[i]->name,
732               rset->rset_terms[i]->nn,
733               rset->rset_terms[i]->flags);
734
735     rank_class = zebraRankLookup (zh, "rank-1");
736     rc = rank_class->control;
737
738     if (rset_read (rset, rfd, &key, &term_index))
739     {
740         int psysno = key.sysno;
741         int score;
742         void *handle =
743             (*rc->begin) (zh, rank_class->class_handle, rset);
744         (zebraSet->hits)++;
745         do
746         {
747             kno++;
748             if (key.sysno != psysno)
749             {
750                 score = (*rc->calc) (handle, psysno);
751
752                 resultSetInsertRank (zh, sort_info, psysno, score, 'A');
753                 (zebraSet->hits)++;
754                 psysno = key.sysno;
755             }
756             (*rc->add) (handle, key.seqno, term_index);
757         }
758         while (rset_read (rset, rfd, &key, &term_index));
759         score = (*rc->calc) (handle, psysno);
760         resultSetInsertRank (zh, sort_info, psysno, score, 'A');
761         (*rc->end) (zh, handle);
762     }
763     rset_close (rset, rfd);
764     logf (LOG_DEBUG, "%d keys, %d distinct sysnos", kno, zebraSet->hits);
765 }
766
767 ZebraRankClass zebraRankLookup (ZebraHandle zh, const char *name)
768 {
769     ZebraRankClass p = zh->service->rank_classes;
770     while (p && strcmp (p->control->name, name))
771         p = p->next;
772     if (p && !p->init_flag)
773     {
774         if (p->control->create)
775             p->class_handle = (*p->control->create)(zh->service);
776         p->init_flag = 1;
777     }
778     return p;
779 }
780
781 void zebraRankInstall (ZebraService zh, struct rank_control *ctrl)
782 {
783     ZebraRankClass p = (ZebraRankClass) xmalloc (sizeof(*p));
784     p->control = (struct rank_control *) xmalloc (sizeof(*p->control));
785     memcpy (p->control, ctrl, sizeof(*p->control));
786     p->control->name = xstrdup (ctrl->name);
787     p->init_flag = 0;
788     p->next = zh->rank_classes;
789     zh->rank_classes = p;
790 }
791
792 void zebraRankDestroy (ZebraService zh)
793 {
794     ZebraRankClass p = zh->rank_classes;
795     while (p)
796     {
797         ZebraRankClass p_next = p->next;
798         if (p->init_flag && p->control->destroy)
799             (*p->control->destroy)(zh, p->class_handle);
800         xfree (p->control->name);
801         xfree (p->control);
802         xfree (p);
803         p = p_next;
804     }
805     zh->rank_classes = NULL;
806 }