Do not use obsolete YAZ defines
[idzebra-moved-to-github.git] / index / zsets.c
1 /* $Id: zsets.c,v 1.47 2004-05-10 08:47:54 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         yaz_log (LOG_DEBUG, "adding result set %s", name);
211         s = (ZebraSet) xmalloc (sizeof(*s));
212         s->next = zh->sets;
213         zh->sets = s;
214         s->name = (char *) xmalloc (strlen(name)+1);
215         strcpy (s->name, name);
216
217         s->sort_info = (struct zset_sort_info *)
218             xmalloc (sizeof(*s->sort_info));
219         s->sort_info->max_entries = 1000;
220         s->sort_info->entries = (struct zset_sort_entry **)
221             xmalloc (sizeof(*s->sort_info->entries) *
222                      s->sort_info->max_entries);
223         s->sort_info->all_entries = (struct zset_sort_entry *)
224             xmalloc (sizeof(*s->sort_info->all_entries) *
225                      s->sort_info->max_entries);
226         for (i = 0; i < s->sort_info->max_entries; i++)
227             s->sort_info->entries[i] = s->sort_info->all_entries + i;
228     }
229     s->locked = 0;
230     s->term_entries = 0;
231     s->hits = 0;
232     s->rset = 0;
233     s->nmem = 0;
234     s->rpn = 0;
235     return s;
236 }
237
238 ZebraSet resultSetGet (ZebraHandle zh, const char *name)
239 {
240     ZebraSet s;
241
242     for (s = zh->sets; s; s = s->next)
243         if (!strcmp (s->name, name))
244         {
245             if (!s->term_entries && !s->rset && s->rpn)
246             {
247                 NMEM nmem = nmem_create ();
248                 yaz_log (LOG_LOG, "research %s", name);
249                 s->rset =
250                     rpn_search (zh, nmem, s->rpn, s->num_bases,
251                                 s->basenames, s->name, s);
252                 nmem_destroy (nmem);
253             }
254             return s;
255         }
256     return NULL;
257 }
258
259 void resultSetInvalidate (ZebraHandle zh)
260 {
261     ZebraSet s = zh->sets;
262     
263     for (; s; s = s->next)
264     {
265         if (s->rset)
266             rset_delete (s->rset);
267         s->rset = 0;
268     }
269 }
270
271 void resultSetDestroy (ZebraHandle zh, int num, char **names,int *statuses)
272 {
273     ZebraSet * ss = &zh->sets;
274     int i;
275     
276     if (statuses)
277         for (i = 0; i<num; i++)
278             statuses[i] = Z_DeleteStatus_resultSetDidNotExist;
279     zh->errCode = 0;
280     zh->errString = NULL;
281     while (*ss)
282     {
283         int i = -1;
284         ZebraSet s = *ss;
285         if (num >= 0)
286         {
287             for (i = 0; i<num; i++)
288                 if (!strcmp (s->name, names[i]))
289                 {
290                     if (statuses)
291                         statuses[i] = Z_DeleteStatus_success;
292                     i = -1;
293                     break;
294                 }
295         }
296         if (i < 0)
297         {
298             *ss = s->next;
299             
300             xfree (s->sort_info->all_entries);
301             xfree (s->sort_info->entries);
302             xfree (s->sort_info);
303             
304             if (s->nmem)
305                 nmem_destroy (s->nmem);
306             if (s->rset)
307                 rset_delete (s->rset);
308             xfree (s->name);
309             xfree (s);
310         }
311         else
312             ss = &s->next;
313     }
314 }
315
316 ZebraPosSet zebraPosSetCreate (ZebraHandle zh, const char *name, 
317                                int num, int *positions)
318 {
319     ZebraSet sset;
320     ZebraPosSet sr = 0;
321     RSET rset;
322     int i;
323     struct zset_sort_info *sort_info;
324
325     if (!(sset = resultSetGet (zh, name)))
326         return NULL;
327     if (!(rset = sset->rset))
328     {
329         if (!sset->term_entries)
330             return 0;
331         sr = (ZebraPosSet) xmalloc (sizeof(*sr) * num);
332         for (i = 0; i<num; i++)
333         {
334             sr[i].sysno = 0;
335             sr[i].score = -1;
336             sr[i].term = 0;
337             sr[i].db = 0;
338
339             if (positions[i] <= sset->term_entries_max)
340             {
341                 sr[i].term = sset->term_entries[positions[i]-1].term;
342                 sr[i].db = sset->term_entries[positions[i]-1].db;
343             }
344         }
345     }
346     else
347     {
348         sr = (ZebraPosSet) xmalloc (sizeof(*sr) * num);
349         for (i = 0; i<num; i++)
350         {
351             sr[i].sysno = 0;
352             sr[i].score = -1;
353             sr[i].term = 0;
354             sr[i].db = 0;
355         }
356         sort_info = sset->sort_info;
357         if (sort_info)
358         {
359             int position;
360             
361             for (i = 0; i<num; i++)
362             {
363                 position = positions[i];
364                 if (position > 0 && position <= sort_info->num_entries)
365                 {
366                     yaz_log (LOG_DEBUG, "got pos=%d (sorted)", position);
367                     sr[i].sysno = sort_info->entries[position-1]->sysno;
368                     sr[i].score = sort_info->entries[position-1]->score;
369                 }
370             }
371         }
372         /* did we really get all entries using sort ? */
373         for (i = 0; i<num; i++)
374         {
375             if (!sr[i].sysno)
376                 break;
377         }
378         if (i < num) /* nope, get the rest, unsorted - sorry */
379         {
380             int position = 0;
381             int num_i = 0;
382             int psysno = 0;
383             int term_index;
384             RSFD rfd;
385             struct it_key key;
386             
387             if (sort_info)
388                 position = sort_info->num_entries;
389             while (num_i < num && positions[num_i] < position)
390                 num_i++;
391             rfd = rset_open (rset, RSETF_READ);
392             while (num_i < num && rset_read (rset, rfd, &key, &term_index))
393             {
394                 if (key.sysno != psysno)
395                 {
396                     psysno = key.sysno;
397                     if (sort_info)
398                     {
399                         /* determine we alreay have this in our set */
400                         for (i = sort_info->num_entries; --i >= 0; )
401                             if (psysno == sort_info->entries[i]->sysno)
402                                 break;
403                         if (i >= 0)
404                             continue;
405                     }
406                     position++;
407                     assert (num_i < num);
408                     if (position == positions[num_i])
409                     {
410                         sr[num_i].sysno = psysno;
411                         yaz_log (LOG_DEBUG, "got pos=%d (unsorted)", position);
412                         sr[num_i].score = -1;
413                         num_i++;
414                     }
415                 }
416             }
417             rset_close (rset, rfd);
418         }
419     }
420     return sr;
421 }
422
423 void zebraPosSetDestroy (ZebraHandle zh, ZebraPosSet records, int num)
424 {
425     xfree (records);
426 }
427
428 struct sortKeyInfo {
429     int relation;
430     int attrUse;
431     int numerical;
432 };
433
434 void resultSetInsertSort (ZebraHandle zh, ZebraSet sset,
435                           struct sortKeyInfo *criteria, int num_criteria,
436                           int sysno)
437 {
438     struct zset_sort_entry this_entry;
439     struct zset_sort_entry *new_entry = NULL;
440     struct zset_sort_info *sort_info = sset->sort_info;
441     int i, j;
442
443     sortIdx_sysno (zh->reg->sortIdx, sysno);
444     for (i = 0; i<num_criteria; i++)
445     {
446         sortIdx_type (zh->reg->sortIdx, criteria[i].attrUse);
447         sortIdx_read (zh->reg->sortIdx, this_entry.buf[i]);
448     }
449     i = sort_info->num_entries;
450     while (--i >= 0)
451     {
452         int rel = 0;
453         for (j = 0; j<num_criteria; j++)
454         {
455             if (criteria[j].numerical)
456             {
457                 double diff = atof(this_entry.buf[j]) -
458                               atof(sort_info->entries[i]->buf[j]);
459                 rel = 0;
460                 if (diff > 0.0)
461                     rel = 1;
462                 else if (diff < 0.0)
463                     rel = -1;
464             }
465             else
466             {
467                 rel = memcmp (this_entry.buf[j], sort_info->entries[i]->buf[j],
468                           SORT_IDX_ENTRYSIZE);
469             }
470             if (rel)
471                 break;
472         }       
473         if (!rel)
474             break;
475         if (criteria[j].relation == 'A')
476         {
477             if (rel > 0)
478                 break;
479         }
480         else if (criteria[j].relation == 'D')
481         {
482             if (rel < 0)
483                 break;
484         }
485     }
486     ++i;
487     j = sort_info->max_entries;
488     if (i == j)
489         return;
490
491     if (sort_info->num_entries == j)
492         --j;
493     else
494         j = (sort_info->num_entries)++;
495     new_entry = sort_info->entries[j];
496     while (j != i)
497     {
498         sort_info->entries[j] = sort_info->entries[j-1];
499         --j;
500     }
501     sort_info->entries[i] = new_entry;
502     assert (new_entry);
503     for (i = 0; i<num_criteria; i++)
504         memcpy (new_entry->buf[i], this_entry.buf[i], SORT_IDX_ENTRYSIZE);
505     new_entry->sysno = sysno;
506     new_entry->score = -1;
507 }
508
509 void resultSetInsertRank (ZebraHandle zh, struct zset_sort_info *sort_info,
510                           int sysno, int score, int relation)
511 {
512     struct zset_sort_entry *new_entry = NULL;
513     int i, j;
514
515     i = sort_info->num_entries;
516     while (--i >= 0)
517     {
518         int rel = 0;
519
520         rel = score - sort_info->entries[i]->score;
521
522         if (relation == 'D')
523         {
524             if (rel >= 0)
525                 break;
526         }
527         else if (relation == 'A')
528         {
529             if (rel <= 0)
530                 break;
531         }
532     }
533     ++i;
534     j = sort_info->max_entries;
535     if (i == j)
536         return;
537
538     if (sort_info->num_entries == j)
539         --j;
540     else
541         j = (sort_info->num_entries)++;
542     
543     new_entry = sort_info->entries[j];
544     while (j != i)
545     {
546         sort_info->entries[j] = sort_info->entries[j-1];
547         --j;
548     }
549     sort_info->entries[i] = new_entry;
550     assert (new_entry);
551     new_entry->sysno = sysno;
552     new_entry->score = score;
553 }
554
555 void resultSetSort (ZebraHandle zh, NMEM nmem,
556                     int num_input_setnames, const char **input_setnames,
557                     const char *output_setname,
558                     Z_SortKeySpecList *sort_sequence, int *sort_status)
559 {
560     ZebraSet sset;
561     RSET rset;
562
563     if (num_input_setnames == 0)
564     {
565         zh->errCode = 208;
566         return ;
567     }
568     if (num_input_setnames > 1)
569     {
570         zh->errCode = 230;
571         return;
572     }
573     yaz_log (LOG_DEBUG, "result set sort input=%s output=%s",
574           *input_setnames, output_setname);
575     sset = resultSetGet (zh, input_setnames[0]);
576     if (!sset)
577     {
578         zh->errCode = 30;
579         zh->errString = nmem_strdup (nmem, input_setnames[0]);
580         return;
581     }
582     if (!(rset = sset->rset))
583     {
584         zh->errCode = 30;
585         zh->errString = nmem_strdup (nmem, input_setnames[0]);
586         return;
587     }
588     if (strcmp (output_setname, input_setnames[0]))
589     {
590         rset = rset_dup (rset);
591         sset = resultSetAdd (zh, output_setname, 1);
592         sset->rset = rset;
593     }
594     resultSetSortSingle (zh, nmem, sset, rset, sort_sequence, sort_status);
595 }
596
597 void resultSetSortSingle (ZebraHandle zh, NMEM nmem,
598                           ZebraSet sset, RSET rset,
599                           Z_SortKeySpecList *sort_sequence, int *sort_status)
600 {
601     int i, psysno = 0;
602     struct it_key key;
603     struct sortKeyInfo sort_criteria[3];
604     int num_criteria;
605     int term_index;
606     RSFD rfd;
607
608     yaz_log (LOG_LOG, "resultSetSortSingle start");
609     sset->sort_info->num_entries = 0;
610
611     sset->hits = 0;
612     num_criteria = sort_sequence->num_specs;
613     if (num_criteria > 3)
614         num_criteria = 3;
615     for (i = 0; i < num_criteria; i++)
616     {
617         Z_SortKeySpec *sks = sort_sequence->specs[i];
618         Z_SortKey *sk;
619
620         if (*sks->sortRelation == Z_SortKeySpec_ascending)
621             sort_criteria[i].relation = 'A';
622         else if (*sks->sortRelation == Z_SortKeySpec_descending)
623             sort_criteria[i].relation = 'D';
624         else
625         {
626             zh->errCode = 214;
627             return;
628         }
629         if (sks->sortElement->which == Z_SortElement_databaseSpecific)
630         {
631             zh->errCode = 210;
632             return;
633         }
634         else if (sks->sortElement->which != Z_SortElement_generic)
635         {
636             zh->errCode = 237;
637             return;
638         }       
639         sk = sks->sortElement->u.generic;
640         switch (sk->which)
641         {
642         case Z_SortKey_sortField:
643             yaz_log (LOG_DEBUG, "Sort: key %d is of type sortField", i+1);
644             zh->errCode = 207;
645             return;
646         case Z_SortKey_elementSpec:
647             yaz_log (LOG_DEBUG, "Sort: key %d is of type elementSpec", i+1);
648             zh->errCode = 207;
649             return;
650         case Z_SortKey_sortAttributes:
651             yaz_log (LOG_DEBUG, "Sort: key %d is of type sortAttributes", i+1);
652             sort_criteria[i].attrUse =
653                 zebra_maps_sort (zh->reg->zebra_maps,
654                                  sk->u.sortAttributes,
655                                  &sort_criteria[i].numerical);
656             yaz_log (LOG_DEBUG, "use value = %d", sort_criteria[i].attrUse);
657             if (sort_criteria[i].attrUse == -1)
658             {
659                 zh->errCode = 116;
660                 return;
661             }
662             if (sortIdx_type (zh->reg->sortIdx, sort_criteria[i].attrUse))
663             {
664                 zh->errCode = 207;
665                 return;
666             }
667             break;
668         }
669     }
670     rfd = rset_open (rset, RSETF_READ);
671     while (rset_read (rset, rfd, &key, &term_index))
672     {
673         if (key.sysno != psysno)
674         {
675             (sset->hits)++;
676             psysno = key.sysno;
677             resultSetInsertSort (zh, sset,
678                                  sort_criteria, num_criteria, psysno);
679         }
680     }
681     rset_close (rset, rfd);
682
683     for (i = 0; i < rset->no_rset_terms; i++)
684         yaz_log (LOG_LOG, "term=\"%s\" nn=%d type=%s count=%d",
685                  rset->rset_terms[i]->name,
686                  rset->rset_terms[i]->nn,
687                  rset->rset_terms[i]->flags,
688                  rset->rset_terms[i]->count);
689
690     *sort_status = Z_SortResponse_success;
691     yaz_log (LOG_LOG, "resultSetSortSingle end");
692 }
693
694 RSET resultSetRef (ZebraHandle zh, const char *resultSetId)
695 {
696     ZebraSet s;
697
698     if ((s = resultSetGet (zh, resultSetId)))
699         return s->rset;
700     return NULL;
701 }
702
703 void resultSetRank (ZebraHandle zh, ZebraSet zebraSet, RSET rset)
704 {
705     int kno = 0;
706     struct it_key key;
707     RSFD rfd;
708     int term_index, i;
709     ZebraRankClass rank_class;
710     struct rank_control *rc;
711     struct zset_sort_info *sort_info;
712
713     sort_info = zebraSet->sort_info;
714     sort_info->num_entries = 0;
715     zebraSet->hits = 0;
716     rfd = rset_open (rset, RSETF_READ);
717
718     yaz_log (LOG_LOG, "resultSetRank");
719
720     rank_class = zebraRankLookup (zh, res_get_def(zh->res, "rank", "rank-1"));
721     rc = rank_class->control;
722
723     if (rset_read (rset, rfd, &key, &term_index))
724     {
725         int psysno = key.sysno;
726         int score;
727         void *handle =
728             (*rc->begin) (zh->reg, rank_class->class_handle, rset);
729         (zebraSet->hits)++;
730         do
731         {
732             kno++;
733             if (key.sysno != psysno)
734             {
735                 score = (*rc->calc) (handle, psysno);
736
737                 resultSetInsertRank (zh, sort_info, psysno, score, 'A');
738                 (zebraSet->hits)++;
739                 psysno = key.sysno;
740             }
741             (*rc->add) (handle, key.seqno, term_index);
742         }
743         while (rset_read (rset, rfd, &key, &term_index));
744         score = (*rc->calc) (handle, psysno);
745         resultSetInsertRank (zh, sort_info, psysno, score, 'A');
746         (*rc->end) (zh->reg, handle);
747     }
748     rset_close (rset, rfd);
749
750     for (i = 0; i < rset->no_rset_terms; i++)
751         yaz_log (LOG_LOG, "term=\"%s\" nn=%d type=%s count=%d",
752                  rset->rset_terms[i]->name,
753                  rset->rset_terms[i]->nn,
754                  rset->rset_terms[i]->flags,
755                  rset->rset_terms[i]->count);
756     
757     yaz_log (LOG_LOG, "%d keys, %d distinct sysnos", kno, zebraSet->hits);
758 }
759
760 ZebraRankClass zebraRankLookup (ZebraHandle zh, const char *name)
761 {
762     ZebraRankClass p = zh->reg->rank_classes;
763     while (p && strcmp (p->control->name, name))
764         p = p->next;
765     if (p && !p->init_flag)
766     {
767         if (p->control->create)
768             p->class_handle = (*p->control->create)(zh);
769         p->init_flag = 1;
770     }
771     return p;
772 }
773
774 void zebraRankInstall (struct zebra_register *reg, struct rank_control *ctrl)
775 {
776     ZebraRankClass p = (ZebraRankClass) xmalloc (sizeof(*p));
777     p->control = (struct rank_control *) xmalloc (sizeof(*p->control));
778     memcpy (p->control, ctrl, sizeof(*p->control));
779     p->control->name = xstrdup (ctrl->name);
780     p->init_flag = 0;
781     p->next = reg->rank_classes;
782     reg->rank_classes = p;
783 }
784
785 void zebraRankDestroy (struct zebra_register *reg)
786 {
787     ZebraRankClass p = reg->rank_classes;
788     while (p)
789     {
790         ZebraRankClass p_next = p->next;
791         if (p->init_flag && p->control->destroy)
792             (*p->control->destroy)(reg, p->class_handle);
793         xfree (p->control->name);
794         xfree (p->control);
795         xfree (p);
796         p = p_next;
797     }
798     reg->rank_classes = NULL;
799 }