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