Maximum number of records to be sorted in a result set can be
[idzebra-moved-to-github.git] / index / zsets.c
1 /* $Id: zsets.c,v 1.48 2004-06-07 22:09:32 adam Exp $
2    Copyright (C) 1995,1996,1997,1998,1999,2000,2001,2002,2003,2004
3    Index Data Aps
4
5 This file is part of the Zebra server.
6
7 Zebra is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 2, or (at your option) any later
10 version.
11
12 Zebra is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15 for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with Zebra; see the file LICENSE.zebra.  If not, write to the
19 Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
20 02111-1307, USA.
21 */
22
23
24 #include <stdio.h>
25 #include <assert.h>
26 #ifdef WIN32
27 #include <io.h>
28 #else
29 #include <unistd.h>
30 #endif
31
32 #include "index.h"
33 #include <rstemp.h>
34
35 #define SORT_IDX_ENTRYSIZE 64
36 #define ZSET_SORT_MAX_LEVEL 3
37
38 struct zebra_set_term_entry {
39     int reg_type;
40     char *db;
41     int set;
42     int use;
43     char *term;
44 };
45 struct zebra_set {
46     char *name;
47     RSET rset;
48     NMEM nmem;
49     int hits;
50     int num_bases;
51     char **basenames;
52     Z_RPNQuery *rpn;
53     struct zset_sort_info *sort_info;
54     struct zebra_set_term_entry *term_entries;
55     int term_entries_max;
56     struct zebra_set *next;
57     int locked;
58 };
59
60 struct zset_sort_entry {
61     int sysno;
62     int score;
63     char buf[ZSET_SORT_MAX_LEVEL][SORT_IDX_ENTRYSIZE];
64 };
65
66 struct zset_sort_info {
67     int max_entries;
68     int num_entries;
69     struct zset_sort_entry *all_entries;
70     struct zset_sort_entry **entries;
71 };
72
73 ZebraSet resultSetAddRPN (ZebraHandle zh, NMEM m,
74                           Z_RPNQuery *rpn, int num_bases,
75                           char **basenames, 
76                           const char *setname)
77 {
78     ZebraSet zebraSet;
79     int i;
80
81     zh->errCode = 0;
82     zh->errString = NULL;
83     zh->hits = 0;
84
85     zebraSet = resultSetAdd (zh, setname, 1);
86     if (!zebraSet)
87         return 0;
88     zebraSet->locked = 1;
89     zebraSet->rpn = 0;
90     zebraSet->nmem = m;
91
92     zebraSet->num_bases = num_bases;
93     zebraSet->basenames = 
94         nmem_malloc (zebraSet->nmem, num_bases * sizeof(*zebraSet->basenames));
95     for (i = 0; i<num_bases; i++)
96         zebraSet->basenames[i] = nmem_strdup (zebraSet->nmem, basenames[i]);
97
98
99     zebraSet->rset = rpn_search (zh, zebraSet->nmem, rpn,
100                                  zebraSet->num_bases,
101                                  zebraSet->basenames, zebraSet->name,
102                                  zebraSet);
103     zh->hits = zebraSet->hits;
104     if (zebraSet->rset)
105         zebraSet->rpn = rpn;
106     zebraSet->locked = 0;
107     return zebraSet;
108 }
109
110 void resultSetAddTerm (ZebraHandle zh, ZebraSet s, int reg_type,
111                        const char *db, int set,
112                        int use, const char *term)
113 {
114     if (!s->nmem)
115         s->nmem = nmem_create ();
116     if (!s->term_entries)
117     {
118         int i;
119         s->term_entries_max = 1000;
120         s->term_entries =
121             nmem_malloc (s->nmem, s->term_entries_max * 
122                          sizeof(*s->term_entries));
123         for (i = 0; i < s->term_entries_max; i++)
124             s->term_entries[i].term = 0;
125     }
126     if (s->hits < s->term_entries_max)
127     {
128         s->term_entries[s->hits].reg_type = reg_type;
129         s->term_entries[s->hits].db = nmem_strdup (s->nmem, db);
130         s->term_entries[s->hits].set = set;
131         s->term_entries[s->hits].use = use;
132         s->term_entries[s->hits].term = nmem_strdup (s->nmem, term);
133     }
134     (s->hits)++;
135 }
136
137
138 int zebra_resultSetTerms (ZebraHandle zh, const char *setname, 
139                           int no, int *count, 
140                           int *type, char *out, size_t *len)
141 {
142     ZebraSet s = resultSetGet (zh, setname);
143     int no_max = 0;
144
145     if (count)
146         *count = 0;
147     if (!s || !s->rset)
148         return 0;
149     no_max = s->rset->no_rset_terms;
150     if (no < 0 || no >= no_max)
151         return 0;
152     if (count)
153         *count = s->rset->rset_terms[no]->count;
154     if (type)
155         *type = s->rset->rset_terms[no]->type;
156     
157     if (out)
158     {
159         char *inbuf = s->rset->rset_terms[no]->name;
160         size_t inleft = strlen(inbuf);
161         size_t outleft = *len - 1;
162         int converted = 0;
163
164         if (zh->iconv_from_utf8 != 0)
165         {
166             char *outbuf = out;
167             size_t ret;
168             
169             ret = yaz_iconv(zh->iconv_from_utf8, &inbuf, &inleft,
170                         &outbuf, &outleft);
171             if (ret == (size_t)(-1))
172                 *len = 0;
173             else
174                 *len = outbuf - out;
175             converted = 1;
176         }
177         if (!converted)
178         {
179             if (inleft > outleft)
180                 inleft = outleft;
181             *len = inleft;
182             memcpy (out, inbuf, *len);
183         }
184         out[*len] = 0;
185     }
186     return no_max;
187 }
188
189
190 ZebraSet resultSetAdd (ZebraHandle zh, const char *name, int ov)
191 {
192     ZebraSet s;
193     int i;
194
195     for (s = zh->sets; s; s = s->next)
196         if (!strcmp (s->name, name))
197             break;
198     if (s)
199     {
200         yaz_log (LOG_DEBUG, "updating result set %s", name);
201         if (!ov || s->locked)
202             return NULL;
203         if (s->rset)
204             rset_delete (s->rset);
205         if (s->nmem)
206             nmem_destroy (s->nmem);
207     }
208     else
209     {
210         const char *sort_max_str = zebra_get_resource(zh, "sortmax", "1000");
211
212         yaz_log (LOG_DEBUG, "adding result set %s", name);
213         s = (ZebraSet) xmalloc (sizeof(*s));
214         s->next = zh->sets;
215         zh->sets = s;
216         s->name = (char *) xmalloc (strlen(name)+1);
217         strcpy (s->name, name);
218
219         s->sort_info = (struct zset_sort_info *)
220             xmalloc (sizeof(*s->sort_info));
221         s->sort_info->max_entries = atoi(sort_max_str);
222         if (s->sort_info->max_entries < 2)
223             s->sort_info->max_entries = 2;
224
225         s->sort_info->entries = (struct zset_sort_entry **)
226             xmalloc (sizeof(*s->sort_info->entries) *
227                      s->sort_info->max_entries);
228         s->sort_info->all_entries = (struct zset_sort_entry *)
229             xmalloc (sizeof(*s->sort_info->all_entries) *
230                      s->sort_info->max_entries);
231         for (i = 0; i < s->sort_info->max_entries; i++)
232             s->sort_info->entries[i] = s->sort_info->all_entries + i;
233     }
234     s->locked = 0;
235     s->term_entries = 0;
236     s->hits = 0;
237     s->rset = 0;
238     s->nmem = 0;
239     s->rpn = 0;
240     return s;
241 }
242
243 ZebraSet resultSetGet (ZebraHandle zh, const char *name)
244 {
245     ZebraSet s;
246
247     for (s = zh->sets; s; s = s->next)
248         if (!strcmp (s->name, name))
249         {
250             if (!s->term_entries && !s->rset && s->rpn)
251             {
252                 NMEM nmem = nmem_create ();
253                 yaz_log (LOG_LOG, "research %s", name);
254                 s->rset =
255                     rpn_search (zh, nmem, s->rpn, s->num_bases,
256                                 s->basenames, s->name, s);
257                 nmem_destroy (nmem);
258             }
259             return s;
260         }
261     return NULL;
262 }
263
264 void resultSetInvalidate (ZebraHandle zh)
265 {
266     ZebraSet s = zh->sets;
267     
268     for (; s; s = s->next)
269     {
270         if (s->rset)
271             rset_delete (s->rset);
272         s->rset = 0;
273     }
274 }
275
276 void resultSetDestroy (ZebraHandle zh, int num, char **names,int *statuses)
277 {
278     ZebraSet * ss = &zh->sets;
279     int i;
280     
281     if (statuses)
282         for (i = 0; i<num; i++)
283             statuses[i] = Z_DeleteStatus_resultSetDidNotExist;
284     zh->errCode = 0;
285     zh->errString = NULL;
286     while (*ss)
287     {
288         int i = -1;
289         ZebraSet s = *ss;
290         if (num >= 0)
291         {
292             for (i = 0; i<num; i++)
293                 if (!strcmp (s->name, names[i]))
294                 {
295                     if (statuses)
296                         statuses[i] = Z_DeleteStatus_success;
297                     i = -1;
298                     break;
299                 }
300         }
301         if (i < 0)
302         {
303             *ss = s->next;
304             
305             xfree (s->sort_info->all_entries);
306             xfree (s->sort_info->entries);
307             xfree (s->sort_info);
308             
309             if (s->nmem)
310                 nmem_destroy (s->nmem);
311             if (s->rset)
312                 rset_delete (s->rset);
313             xfree (s->name);
314             xfree (s);
315         }
316         else
317             ss = &s->next;
318     }
319 }
320
321 ZebraPosSet zebraPosSetCreate (ZebraHandle zh, const char *name, 
322                                int num, int *positions)
323 {
324     ZebraSet sset;
325     ZebraPosSet sr = 0;
326     RSET rset;
327     int i;
328     struct zset_sort_info *sort_info;
329
330     if (!(sset = resultSetGet (zh, name)))
331         return NULL;
332     if (!(rset = sset->rset))
333     {
334         if (!sset->term_entries)
335             return 0;
336         sr = (ZebraPosSet) xmalloc (sizeof(*sr) * num);
337         for (i = 0; i<num; i++)
338         {
339             sr[i].sysno = 0;
340             sr[i].score = -1;
341             sr[i].term = 0;
342             sr[i].db = 0;
343
344             if (positions[i] <= sset->term_entries_max)
345             {
346                 sr[i].term = sset->term_entries[positions[i]-1].term;
347                 sr[i].db = sset->term_entries[positions[i]-1].db;
348             }
349         }
350     }
351     else
352     {
353         sr = (ZebraPosSet) xmalloc (sizeof(*sr) * num);
354         for (i = 0; i<num; i++)
355         {
356             sr[i].sysno = 0;
357             sr[i].score = -1;
358             sr[i].term = 0;
359             sr[i].db = 0;
360         }
361         sort_info = sset->sort_info;
362         if (sort_info)
363         {
364             int position;
365             
366             for (i = 0; i<num; i++)
367             {
368                 position = positions[i];
369                 if (position > 0 && position <= sort_info->num_entries)
370                 {
371                     yaz_log (LOG_DEBUG, "got pos=%d (sorted)", position);
372                     sr[i].sysno = sort_info->entries[position-1]->sysno;
373                     sr[i].score = sort_info->entries[position-1]->score;
374                 }
375             }
376         }
377         /* did we really get all entries using sort ? */
378         for (i = 0; i<num; i++)
379         {
380             if (!sr[i].sysno)
381                 break;
382         }
383         if (i < num) /* nope, get the rest, unsorted - sorry */
384         {
385             int position = 0;
386             int num_i = 0;
387             int psysno = 0;
388             int term_index;
389             RSFD rfd;
390             struct it_key key;
391             
392             if (sort_info)
393                 position = sort_info->num_entries;
394             while (num_i < num && positions[num_i] < position)
395                 num_i++;
396             rfd = rset_open (rset, RSETF_READ);
397             while (num_i < num && rset_read (rset, rfd, &key, &term_index))
398             {
399                 if (key.sysno != psysno)
400                 {
401                     psysno = key.sysno;
402                     if (sort_info)
403                     {
404                         /* determine we alreay have this in our set */
405                         for (i = sort_info->num_entries; --i >= 0; )
406                             if (psysno == sort_info->entries[i]->sysno)
407                                 break;
408                         if (i >= 0)
409                             continue;
410                     }
411                     position++;
412                     assert (num_i < num);
413                     if (position == positions[num_i])
414                     {
415                         sr[num_i].sysno = psysno;
416                         yaz_log (LOG_DEBUG, "got pos=%d (unsorted)", position);
417                         sr[num_i].score = -1;
418                         num_i++;
419                     }
420                 }
421             }
422             rset_close (rset, rfd);
423         }
424     }
425     return sr;
426 }
427
428 void zebraPosSetDestroy (ZebraHandle zh, ZebraPosSet records, int num)
429 {
430     xfree (records);
431 }
432
433 struct sortKeyInfo {
434     int relation;
435     int attrUse;
436     int numerical;
437 };
438
439 void resultSetInsertSort (ZebraHandle zh, ZebraSet sset,
440                           struct sortKeyInfo *criteria, int num_criteria,
441                           int sysno)
442 {
443     struct zset_sort_entry this_entry;
444     struct zset_sort_entry *new_entry = NULL;
445     struct zset_sort_info *sort_info = sset->sort_info;
446     int i, j;
447
448     sortIdx_sysno (zh->reg->sortIdx, sysno);
449     for (i = 0; i<num_criteria; i++)
450     {
451         sortIdx_type (zh->reg->sortIdx, criteria[i].attrUse);
452         sortIdx_read (zh->reg->sortIdx, this_entry.buf[i]);
453     }
454     i = sort_info->num_entries;
455     while (--i >= 0)
456     {
457         int rel = 0;
458         for (j = 0; j<num_criteria; j++)
459         {
460             if (criteria[j].numerical)
461             {
462                 double diff = atof(this_entry.buf[j]) -
463                               atof(sort_info->entries[i]->buf[j]);
464                 rel = 0;
465                 if (diff > 0.0)
466                     rel = 1;
467                 else if (diff < 0.0)
468                     rel = -1;
469             }
470             else
471             {
472                 rel = memcmp (this_entry.buf[j], sort_info->entries[i]->buf[j],
473                           SORT_IDX_ENTRYSIZE);
474             }
475             if (rel)
476                 break;
477         }       
478         if (!rel)
479             break;
480         if (criteria[j].relation == 'A')
481         {
482             if (rel > 0)
483                 break;
484         }
485         else if (criteria[j].relation == 'D')
486         {
487             if (rel < 0)
488                 break;
489         }
490     }
491     ++i;
492     j = sort_info->max_entries;
493     if (i == j)
494         return;
495
496     if (sort_info->num_entries == j)
497         --j;
498     else
499         j = (sort_info->num_entries)++;
500     new_entry = sort_info->entries[j];
501     while (j != i)
502     {
503         sort_info->entries[j] = sort_info->entries[j-1];
504         --j;
505     }
506     sort_info->entries[i] = new_entry;
507     assert (new_entry);
508     for (i = 0; i<num_criteria; i++)
509         memcpy (new_entry->buf[i], this_entry.buf[i], SORT_IDX_ENTRYSIZE);
510     new_entry->sysno = sysno;
511     new_entry->score = -1;
512 }
513
514 void resultSetInsertRank (ZebraHandle zh, struct zset_sort_info *sort_info,
515                           int sysno, int score, int relation)
516 {
517     struct zset_sort_entry *new_entry = NULL;
518     int i, j;
519
520     i = sort_info->num_entries;
521     while (--i >= 0)
522     {
523         int rel = 0;
524
525         rel = score - sort_info->entries[i]->score;
526
527         if (relation == 'D')
528         {
529             if (rel >= 0)
530                 break;
531         }
532         else if (relation == 'A')
533         {
534             if (rel <= 0)
535                 break;
536         }
537     }
538     ++i;
539     j = sort_info->max_entries;
540     if (i == j)
541         return;
542
543     if (sort_info->num_entries == j)
544         --j;
545     else
546         j = (sort_info->num_entries)++;
547     
548     new_entry = sort_info->entries[j];
549     while (j != i)
550     {
551         sort_info->entries[j] = sort_info->entries[j-1];
552         --j;
553     }
554     sort_info->entries[i] = new_entry;
555     assert (new_entry);
556     new_entry->sysno = sysno;
557     new_entry->score = score;
558 }
559
560 void resultSetSort (ZebraHandle zh, NMEM nmem,
561                     int num_input_setnames, const char **input_setnames,
562                     const char *output_setname,
563                     Z_SortKeySpecList *sort_sequence, int *sort_status)
564 {
565     ZebraSet sset;
566     RSET rset;
567
568     if (num_input_setnames == 0)
569     {
570         zh->errCode = 208;
571         return ;
572     }
573     if (num_input_setnames > 1)
574     {
575         zh->errCode = 230;
576         return;
577     }
578     yaz_log (LOG_DEBUG, "result set sort input=%s output=%s",
579           *input_setnames, output_setname);
580     sset = resultSetGet (zh, input_setnames[0]);
581     if (!sset)
582     {
583         zh->errCode = 30;
584         zh->errString = nmem_strdup (nmem, input_setnames[0]);
585         return;
586     }
587     if (!(rset = sset->rset))
588     {
589         zh->errCode = 30;
590         zh->errString = nmem_strdup (nmem, input_setnames[0]);
591         return;
592     }
593     if (strcmp (output_setname, input_setnames[0]))
594     {
595         rset = rset_dup (rset);
596         sset = resultSetAdd (zh, output_setname, 1);
597         sset->rset = rset;
598     }
599     resultSetSortSingle (zh, nmem, sset, rset, sort_sequence, sort_status);
600 }
601
602 void resultSetSortSingle (ZebraHandle zh, NMEM nmem,
603                           ZebraSet sset, RSET rset,
604                           Z_SortKeySpecList *sort_sequence, int *sort_status)
605 {
606     int i, psysno = 0;
607     struct it_key key;
608     struct sortKeyInfo sort_criteria[3];
609     int num_criteria;
610     int term_index;
611     RSFD rfd;
612
613     yaz_log (LOG_LOG, "resultSetSortSingle start");
614     sset->sort_info->num_entries = 0;
615
616     sset->hits = 0;
617     num_criteria = sort_sequence->num_specs;
618     if (num_criteria > 3)
619         num_criteria = 3;
620     for (i = 0; i < num_criteria; i++)
621     {
622         Z_SortKeySpec *sks = sort_sequence->specs[i];
623         Z_SortKey *sk;
624
625         if (*sks->sortRelation == Z_SortKeySpec_ascending)
626             sort_criteria[i].relation = 'A';
627         else if (*sks->sortRelation == Z_SortKeySpec_descending)
628             sort_criteria[i].relation = 'D';
629         else
630         {
631             zh->errCode = 214;
632             return;
633         }
634         if (sks->sortElement->which == Z_SortElement_databaseSpecific)
635         {
636             zh->errCode = 210;
637             return;
638         }
639         else if (sks->sortElement->which != Z_SortElement_generic)
640         {
641             zh->errCode = 237;
642             return;
643         }       
644         sk = sks->sortElement->u.generic;
645         switch (sk->which)
646         {
647         case Z_SortKey_sortField:
648             yaz_log (LOG_DEBUG, "Sort: key %d is of type sortField", i+1);
649             zh->errCode = 207;
650             return;
651         case Z_SortKey_elementSpec:
652             yaz_log (LOG_DEBUG, "Sort: key %d is of type elementSpec", i+1);
653             zh->errCode = 207;
654             return;
655         case Z_SortKey_sortAttributes:
656             yaz_log (LOG_DEBUG, "Sort: key %d is of type sortAttributes", i+1);
657             sort_criteria[i].attrUse =
658                 zebra_maps_sort (zh->reg->zebra_maps,
659                                  sk->u.sortAttributes,
660                                  &sort_criteria[i].numerical);
661             yaz_log (LOG_DEBUG, "use value = %d", sort_criteria[i].attrUse);
662             if (sort_criteria[i].attrUse == -1)
663             {
664                 zh->errCode = 116;
665                 return;
666             }
667             if (sortIdx_type (zh->reg->sortIdx, sort_criteria[i].attrUse))
668             {
669                 zh->errCode = 207;
670                 return;
671             }
672             break;
673         }
674     }
675     rfd = rset_open (rset, RSETF_READ);
676     while (rset_read (rset, rfd, &key, &term_index))
677     {
678         if (key.sysno != psysno)
679         {
680             (sset->hits)++;
681             psysno = key.sysno;
682             resultSetInsertSort (zh, sset,
683                                  sort_criteria, num_criteria, psysno);
684         }
685     }
686     rset_close (rset, rfd);
687
688     for (i = 0; i < rset->no_rset_terms; i++)
689         yaz_log (LOG_LOG, "term=\"%s\" nn=%d type=%s count=%d",
690                  rset->rset_terms[i]->name,
691                  rset->rset_terms[i]->nn,
692                  rset->rset_terms[i]->flags,
693                  rset->rset_terms[i]->count);
694
695     *sort_status = Z_SortResponse_success;
696     yaz_log (LOG_LOG, "resultSetSortSingle end");
697 }
698
699 RSET resultSetRef (ZebraHandle zh, const char *resultSetId)
700 {
701     ZebraSet s;
702
703     if ((s = resultSetGet (zh, resultSetId)))
704         return s->rset;
705     return NULL;
706 }
707
708 void resultSetRank (ZebraHandle zh, ZebraSet zebraSet, RSET rset)
709 {
710     int kno = 0;
711     struct it_key key;
712     RSFD rfd;
713     int term_index, i;
714     ZebraRankClass rank_class;
715     struct rank_control *rc;
716     struct zset_sort_info *sort_info;
717
718     sort_info = zebraSet->sort_info;
719     sort_info->num_entries = 0;
720     zebraSet->hits = 0;
721     rfd = rset_open (rset, RSETF_READ);
722
723     yaz_log (LOG_LOG, "resultSetRank");
724
725     rank_class = zebraRankLookup (zh, res_get_def(zh->res, "rank", "rank-1"));
726     rc = rank_class->control;
727
728     if (rset_read (rset, rfd, &key, &term_index))
729     {
730         int psysno = key.sysno;
731         int score;
732         void *handle =
733             (*rc->begin) (zh->reg, rank_class->class_handle, rset);
734         (zebraSet->hits)++;
735         do
736         {
737             kno++;
738             if (key.sysno != psysno)
739             {
740                 score = (*rc->calc) (handle, psysno);
741
742                 resultSetInsertRank (zh, sort_info, psysno, score, 'A');
743                 (zebraSet->hits)++;
744                 psysno = key.sysno;
745             }
746             (*rc->add) (handle, key.seqno, term_index);
747         }
748         while (rset_read (rset, rfd, &key, &term_index));
749         score = (*rc->calc) (handle, psysno);
750         resultSetInsertRank (zh, sort_info, psysno, score, 'A');
751         (*rc->end) (zh->reg, handle);
752     }
753     rset_close (rset, rfd);
754
755     for (i = 0; i < rset->no_rset_terms; i++)
756         yaz_log (LOG_LOG, "term=\"%s\" nn=%d type=%s count=%d",
757                  rset->rset_terms[i]->name,
758                  rset->rset_terms[i]->nn,
759                  rset->rset_terms[i]->flags,
760                  rset->rset_terms[i]->count);
761     
762     yaz_log (LOG_LOG, "%d keys, %d distinct sysnos", kno, zebraSet->hits);
763 }
764
765 ZebraRankClass zebraRankLookup (ZebraHandle zh, const char *name)
766 {
767     ZebraRankClass p = zh->reg->rank_classes;
768     while (p && strcmp (p->control->name, name))
769         p = p->next;
770     if (p && !p->init_flag)
771     {
772         if (p->control->create)
773             p->class_handle = (*p->control->create)(zh);
774         p->init_flag = 1;
775     }
776     return p;
777 }
778
779 void zebraRankInstall (struct zebra_register *reg, struct rank_control *ctrl)
780 {
781     ZebraRankClass p = (ZebraRankClass) xmalloc (sizeof(*p));
782     p->control = (struct rank_control *) xmalloc (sizeof(*p->control));
783     memcpy (p->control, ctrl, sizeof(*p->control));
784     p->control->name = xstrdup (ctrl->name);
785     p->init_flag = 0;
786     p->next = reg->rank_classes;
787     reg->rank_classes = p;
788 }
789
790 void zebraRankDestroy (struct zebra_register *reg)
791 {
792     ZebraRankClass p = reg->rank_classes;
793     while (p)
794     {
795         ZebraRankClass p_next = p->next;
796         if (p->init_flag && p->control->destroy)
797             (*p->control->destroy)(reg, p->class_handle);
798         xfree (p->control->name);
799         xfree (p->control);
800         xfree (p);
801         p = p_next;
802     }
803     reg->rank_classes = NULL;
804 }