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