Updated footer comment
[idzebra-moved-to-github.git] / index / retrieve.c
1 /* This file is part of the Zebra server.
2    Copyright (C) 1994-2009 Index Data
3
4 Zebra is free software; you can redistribute it and/or modify it under
5 the terms of the GNU General Public License as published by the Free
6 Software Foundation; either version 2, or (at your option) any later
7 version.
8
9 Zebra is distributed in the hope that it will be useful, but WITHOUT ANY
10 WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17
18 */
19
20 #include <stdio.h>
21 #include <assert.h>
22
23 #include <fcntl.h>
24 #ifdef WIN32
25 #include <io.h>
26 #include <process.h>
27 #endif
28 #if HAVE_UNISTD_H
29 #include <unistd.h>
30 #endif
31
32 #include "index.h"
33 #include <yaz/diagbib1.h>
34 #include <yaz/snprintf.h>
35 #include <direntz.h>
36 #include <yaz/oid_db.h>
37 #include <zebra_strmap.h>
38
39 #define MAX_SYSNOS_PER_RECORD 40
40
41 #define ZEBRA_XML_HEADER_STR "<record xmlns=\"http://www.indexdata.com/zebra/\""
42
43 struct special_fetch_s {
44     ZebraHandle zh;
45     const char *setname;
46     zint sysno;
47     int score;
48     NMEM nmem;
49 };
50
51 static int zebra_create_record_stream(ZebraHandle zh, 
52                                       Record *rec,
53                                       struct ZebraRecStream *stream)
54 {
55     RecordAttr *recordAttr = rec_init_attr(zh->reg->zei, *rec);
56
57     if ((*rec)->size[recInfo_storeData] > 0 
58         || (*rec)->info[recInfo_filename] == 0)
59         zebra_create_stream_mem(stream, (*rec)->info[recInfo_storeData],
60                                 (*rec)->size[recInfo_storeData]);
61     else
62     {
63         char full_rep[1024];
64         int fd;
65             
66         if (zh->path_reg && !yaz_is_abspath((*rec)->info[recInfo_filename])){
67             strcpy(full_rep, zh->path_reg);
68             strcat(full_rep, "/");
69             strcat(full_rep, (*rec)->info[recInfo_filename]);
70         }
71         else
72             strcpy(full_rep, (*rec)->info[recInfo_filename]);
73             
74         if ((fd = open(full_rep, O_BINARY|O_RDONLY)) == -1){
75             yaz_log(YLOG_WARN|YLOG_ERRNO, "Retrieve fail; missing file: %s",
76                      full_rep);
77             rec_free(rec);
78             return YAZ_BIB1_SYSTEM_ERROR_IN_PRESENTING_RECORDS;
79         }
80         zebra_create_stream_fd(stream, fd, recordAttr->recordOffset);
81     }
82     return 0;
83 }
84    
85
86 struct index_spec {
87     const char *index_name;
88     const char *index_type;
89     const char *extra;
90     struct index_spec *next;
91 };
92
93
94 struct index_spec *parse_index_spec(const char *elem, NMEM nmem,
95                                     int *error)
96 {
97     struct index_spec *first = 0;
98     struct index_spec **last = &first;
99     const char *cp = elem;
100
101     *error = 0;
102     if (cp[0] == ':' && cp[1] == ':')
103     {
104
105         cp++; /* skip first ':' */
106
107         for (;;)
108         {
109             const char *cp0;
110             struct index_spec *spec = nmem_malloc(nmem, sizeof(*spec));
111             spec->index_type = 0;
112             spec->next = 0;
113             spec->extra = 0;
114
115             if (!first)
116                 first = spec;
117             *last = spec;
118             last = &spec->next;
119
120             cp++; /* skip ',' or second ':' */
121             cp0 = cp;
122             while (*cp != ':' && *cp != '\0' && *cp != ',')
123                 cp++;
124             spec->index_name = nmem_strdupn(nmem, cp0, cp - cp0);
125             if (*cp == ':') /* type as well */
126             {
127                 cp++;
128                 cp0 = cp;
129                 
130                 while (*cp != '\0' && *cp != ',' && *cp != ':')
131                     cp++;
132                 spec->index_type = nmem_strdupn(nmem, cp0, cp - cp0);
133             }
134             if (*cp == ':') /* extra arguments */
135             {
136                 cp++;
137                 cp0 = cp;
138                 
139                 while (*cp != '\0' && *cp != ',' && *cp != ':')
140                     cp++;
141                 spec->extra = nmem_strdupn(nmem, cp0, cp - cp0);
142             }
143             if (*cp != ',')
144                 break;
145         }
146     }
147     if (*cp != '\0')
148         *error = 1;
149     return first;
150 }
151                             
152 static int parse_zebra_elem(const char *elem,
153                             const char **index, size_t *index_len,
154                             const char **type, size_t *type_len)
155 {
156     *index = 0;
157     *index_len = 0;
158
159     *type = 0;
160     *type_len = 0;
161
162     if (elem && *elem)
163     {
164         char *cp;
165         /* verify that '::' is in the beginning of *elem 
166            and something more follows */
167         if (':' != *elem
168             || !(elem +1) || ':' != *(elem +1)
169             || !(elem +2) || '\0' == *(elem +2))
170             return 0;
171  
172         /* pick out info from string after '::' */
173         elem = elem + 2;
174         cp = strchr(elem, ':');
175
176         if (!cp) /* index, no colon, no type */
177         {
178             *index = elem;
179             *index_len = strlen(elem);
180         }
181         else if (cp[1] == '\0') /* colon, but no following type */
182         {
183             return 0;
184         }
185         else  /* index, colon and type */
186         {
187             *index = elem;
188             *index_len = cp - elem;
189             *type = cp+1;
190             *type_len = strlen(cp+1);
191         }
192     }
193     return 1;
194 }
195
196
197 static int sort_fetch(
198     struct special_fetch_s *fi, const char *elemsetname,
199     const Odr_oid *input_format,
200     const Odr_oid **output_format,
201     WRBUF result, WRBUF addinfo)
202 {
203     const char *retrieval_index;
204     size_t retrieval_index_len; 
205     const char *retrieval_type;
206     size_t retrieval_type_len;
207     char retrieval_index_cstr[256];
208     char retrieval_type_cstr[256];
209     int ord;
210     ZebraHandle zh = fi->zh;
211
212     if (!parse_zebra_elem(elemsetname,
213                           &retrieval_index, &retrieval_index_len,
214                           &retrieval_type,  &retrieval_type_len))
215     {
216         return YAZ_BIB1_SPECIFIED_ELEMENT_SET_NAME_NOT_VALID_FOR_SPECIFIED_;
217     }
218     
219     if (retrieval_type_len == 0)
220         return -1;   /* must have a register type specified */
221     if (!retrieval_index_len ||
222         retrieval_index_len >= sizeof(retrieval_index_cstr)-1)
223     {
224         return YAZ_BIB1_SPECIFIED_ELEMENT_SET_NAME_NOT_VALID_FOR_SPECIFIED_;
225     }
226         
227     memcpy(retrieval_index_cstr, retrieval_index, retrieval_index_len);
228     retrieval_index_cstr[retrieval_index_len] = '\0';
229
230     memcpy(retrieval_type_cstr, retrieval_type, retrieval_type_len);
231     retrieval_type_cstr[retrieval_type_len] = '\0';
232
233     ord = zebraExplain_lookup_attr_str(zh->reg->zei,
234                                        zinfo_index_category_sort,
235                                        retrieval_type_cstr,
236                                        retrieval_index_cstr);
237     if (ord == -1)
238         return -1;  /* is not a sort index */
239     else
240     {
241         WRBUF wrbuf_str = wrbuf_alloc();
242         const char *index_type;
243         const char *db = 0;
244         const char *string_index = 0;
245         WRBUF wrbuf_result = result;
246         int off = 0;
247         
248         zebraExplain_lookup_ord(zh->reg->zei, ord, &index_type, &db, 
249                                 &string_index);
250         if (!oid_oidcmp(input_format, yaz_oid_recsyn_xml))
251         {
252             *output_format = yaz_oid_recsyn_xml;
253             wrbuf_printf(wrbuf_result, ZEBRA_XML_HEADER_STR
254                          " sysno=\"" ZINT_FORMAT "\""
255                          " set=\"zebra::index%s/\">\n",
256                          fi->sysno, elemsetname);
257         }
258         else if (!oid_oidcmp(input_format, yaz_oid_recsyn_sutrs))
259         {
260             *output_format = yaz_oid_recsyn_sutrs;
261         }
262         else
263         {
264             yaz_log(YLOG_WARN, "unsupported format for element set zebra::%s", 
265                     elemsetname);
266             *output_format = 0;
267             wrbuf_destroy(wrbuf_str);
268             return YAZ_BIB1_NO_SYNTAXES_AVAILABLE_FOR_THIS_REQUEST;
269         }
270         zebra_sort_type(zh->reg->sort_index, ord);
271         zebra_sort_sysno(zh->reg->sort_index, fi->sysno);
272         zebra_sort_read(zh->reg->sort_index, 0, wrbuf_str);
273
274         while (off != wrbuf_len(wrbuf_str))
275         {
276             char dst_buf[IT_MAX_WORD];
277             assert(off < wrbuf_len(wrbuf_str));
278             zebra_term_untrans(zh, index_type, dst_buf,
279                                wrbuf_buf(wrbuf_str)+off);
280             
281             if (!oid_oidcmp(input_format, yaz_oid_recsyn_xml))
282             {
283                 wrbuf_printf(wrbuf_result, "  <index name=\"%s\"", 
284                              string_index);
285                 wrbuf_printf(wrbuf_result, " type=\"%s\">", index_type);
286                 wrbuf_xmlputs(wrbuf_result, dst_buf);
287                 wrbuf_printf(wrbuf_result, "</index>\n");
288             }
289             else if (!oid_oidcmp(input_format, yaz_oid_recsyn_sutrs))
290             {
291                 wrbuf_printf(wrbuf_result, "%s %s %s\n", string_index, index_type,
292                              dst_buf);
293             }
294             off += strlen(wrbuf_buf(wrbuf_str)+off) + 1;
295         }
296         if (!oid_oidcmp(input_format, yaz_oid_recsyn_xml))
297         {
298             wrbuf_printf(wrbuf_result, "</record>\n");
299         }
300         wrbuf_destroy(wrbuf_str);
301         return 0;
302     }
303 }
304                             
305 static int special_index_fetch(
306     struct special_fetch_s *fi, const char *elemsetname,
307     const Odr_oid *input_format,
308     const Odr_oid **output_format,
309     WRBUF result, WRBUF addinfo,
310     Record rec)
311 {
312     const char *retrieval_index;
313     size_t retrieval_index_len; 
314     const char *retrieval_type;
315     size_t retrieval_type_len;
316     zebra_rec_keys_t keys;
317     int ret_code = 0;
318     char retrieval_type_cstr[256];
319     ZebraHandle zh = fi->zh;
320     
321     /* set output variables before processing possible error states */
322     /* *rec_lenp = 0; */
323
324     /* only accept XML and SUTRS requests */
325     if (oid_oidcmp(input_format, yaz_oid_recsyn_xml)
326         && oid_oidcmp(input_format, yaz_oid_recsyn_sutrs))
327     {
328         yaz_log(YLOG_WARN, "unsupported format for element set zebra::%s", 
329                 elemsetname);
330         *output_format = 0;
331         return YAZ_BIB1_NO_SYNTAXES_AVAILABLE_FOR_THIS_REQUEST;
332     }
333
334     if (!parse_zebra_elem(elemsetname,
335                      &retrieval_index, &retrieval_index_len,
336                      &retrieval_type,  &retrieval_type_len))
337         return YAZ_BIB1_SPECIFIED_ELEMENT_SET_NAME_NOT_VALID_FOR_SPECIFIED_;
338
339     if (retrieval_type_len)
340     {
341         memcpy(retrieval_type_cstr, retrieval_type, retrieval_type_len);
342         retrieval_type_cstr[retrieval_type_len] = '\0';
343     }
344     
345     if (retrieval_index_len)
346     {
347         char retrieval_index_cstr[256];
348
349         if (retrieval_index_len < sizeof(retrieval_index_cstr) -1)
350         {
351             memcpy(retrieval_index_cstr, retrieval_index, retrieval_index_len);
352             retrieval_index_cstr[retrieval_index_len] = '\0';
353             
354             if (zebraExplain_lookup_attr_str(zh->reg->zei,
355                                              zinfo_index_category_index,
356                                              (retrieval_type_len == 0 ? 0 : 
357                                               retrieval_type_cstr),
358                                              retrieval_index_cstr) == -1)
359                 return YAZ_BIB1_SPECIFIED_ELEMENT_SET_NAME_NOT_VALID_FOR_SPECIFIED_;
360         }
361     }
362
363     keys = zebra_rec_keys_open();
364     zebra_rec_keys_set_buf(keys, rec->info[recInfo_delKeys],
365                            rec->size[recInfo_delKeys], 0);
366
367     if (!zebra_rec_keys_rewind(keys))
368     {
369         ret_code = YAZ_BIB1_SYSTEM_ERROR_IN_PRESENTING_RECORDS;
370     }
371     else
372     {
373         size_t slen;
374         const char *str;
375         struct it_key key_in;
376         WRBUF wrbuf = result;
377     
378         if (!oid_oidcmp(input_format, yaz_oid_recsyn_xml))
379         {
380             *output_format = input_format;
381             wrbuf_printf(wrbuf, ZEBRA_XML_HEADER_STR
382                          " sysno=\"" ZINT_FORMAT "\""
383                          " set=\"zebra::index%s/\">\n",
384                          fi->sysno, elemsetname);
385         }
386         else if (!oid_oidcmp(input_format, yaz_oid_recsyn_sutrs))
387             *output_format = input_format;
388
389         while (zebra_rec_keys_read(keys, &str, &slen, &key_in))
390         {
391             int i;
392             int ord = CAST_ZINT_TO_INT(key_in.mem[0]);
393             const char *index_type;
394             const char *db = 0;
395             const char *string_index = 0;
396             size_t string_index_len;
397             char dst_buf[IT_MAX_WORD];
398             
399             zebraExplain_lookup_ord(zh->reg->zei, ord, &index_type, &db,
400                                     &string_index);
401             string_index_len = strlen(string_index);
402
403             /* process only if index is not defined, 
404                or if defined and matching */
405             if (retrieval_index == 0 
406                 || (string_index_len == retrieval_index_len 
407                     && !memcmp(string_index, retrieval_index,
408                                string_index_len)))
409             {
410                 /* process only if type is not defined, or is matching */
411                 if (retrieval_type == 0 
412                     || !strcmp(retrieval_type_cstr, index_type))
413                 {
414                     if (zebra_term_untrans(zh, index_type, dst_buf, str))
415                         *dst_buf = '\0'; /* untrans failed */
416
417                     if (!oid_oidcmp(input_format, yaz_oid_recsyn_xml))
418                     {
419                         wrbuf_printf(wrbuf, "  <index name=\"%s\"", 
420                                      string_index);
421                         
422                         wrbuf_printf(wrbuf, " type=\"%s\"", index_type);
423                         
424                         wrbuf_printf(wrbuf, " seq=\"" ZINT_FORMAT "\">", 
425                                      key_in.mem[key_in.len -1]);
426                         wrbuf_xmlputs(wrbuf, dst_buf);
427                         wrbuf_printf(wrbuf, "</index>\n");
428                     }
429                     else 
430                     {
431                         wrbuf_printf(wrbuf, "%s ", string_index);
432                         
433                         wrbuf_printf(wrbuf, "%s", index_type);
434                         
435                         for (i = 1; i < key_in.len; i++)
436                             wrbuf_printf(wrbuf, " " ZINT_FORMAT, 
437                                              key_in.mem[i]);
438                         
439                         wrbuf_printf(wrbuf, " %s", dst_buf);
440                         
441                         wrbuf_printf(wrbuf, "\n");
442
443                     }
444                     
445                 }
446             }
447         }
448         if (!oid_oidcmp(input_format, yaz_oid_recsyn_xml))
449             wrbuf_printf(wrbuf, "</record>\n");
450     }
451     zebra_rec_keys_close(keys);
452     return ret_code;
453 }
454
455
456 static void retrieve_puts_attr(WRBUF wrbuf, const char *name,
457                                const char *value)
458 {
459     if (value)
460     {
461         wrbuf_printf(wrbuf, " %s=\"", name);
462         wrbuf_xmlputs(wrbuf, value);
463         wrbuf_printf(wrbuf, "\"");
464     }
465 }
466
467 static void retrieve_puts_attr_int(WRBUF wrbuf, const char *name,
468                                const int value)
469 {
470     wrbuf_printf(wrbuf, " %s=\"%i\"", name, value);
471 }
472
473 static void retrieve_puts_str(WRBUF wrbuf, const char *name,
474                                const char *value)
475 {
476     if (value)
477         wrbuf_printf(wrbuf, "%s %s\n", name, value);
478 }
479
480 static void retrieve_puts_int(WRBUF wrbuf, const char *name,
481                                const int value)
482 {
483     wrbuf_printf(wrbuf, "%s %i\n", name, value);
484 }
485
486
487 static void snippet_check_fields(ZebraHandle zh, WRBUF wrbuf,
488                                  zebra_snippets *doc,
489                                  const zebra_snippet_word *doc_w,
490                                  const char *w_index_type)
491 {
492     /* beginning of snippet. See which fields the snippet also
493        occur */
494     const zebra_snippet_word *w;
495     int no = 0;
496     for (w = zebra_snippets_constlist(doc); w; w = w->next)
497     {
498         /* same sequence but other field? */
499         if (w->seqno == doc_w->seqno && w->ord != doc_w->ord)
500         {
501             const char *index_type;
502             const char *db = 0;
503             const char *string_index = 0;
504             
505             zebraExplain_lookup_ord(zh->reg->zei, w->ord, 
506                                     &index_type, &db, &string_index);
507             /* only report for same index type */
508             if (!strcmp(w_index_type, index_type))
509             {
510                 if (no == 0)
511                     wrbuf_printf(wrbuf, " fields=\"%s", string_index);
512                 else
513                     wrbuf_printf(wrbuf, " %s", string_index);
514                 no++;
515             }
516         }
517     }
518     if (no)
519         wrbuf_printf(wrbuf, "\"");
520 }
521
522 static void snippet_xml_record(ZebraHandle zh, WRBUF wrbuf, zebra_snippets *doc)
523 {
524     const zebra_snippet_word *doc_w;
525     int mark_state = 0;
526
527     wrbuf_printf(wrbuf, "%s>\n", ZEBRA_XML_HEADER_STR);
528     for (doc_w = zebra_snippets_constlist(doc); doc_w; doc_w = doc_w->next)
529     {
530         if (doc_w->mark)
531         {
532             const char *index_type;
533             const char *db = 0;
534             const char *string_index = 0;
535
536             zebraExplain_lookup_ord(zh->reg->zei, doc_w->ord, 
537                                     &index_type, &db, &string_index);
538
539             if (mark_state == 0)
540             {
541                 
542                 wrbuf_printf(wrbuf, "  <snippet name=\"%s\"",  string_index);
543                 wrbuf_printf(wrbuf, " type=\"%s\"", index_type);
544                 snippet_check_fields(zh, wrbuf, doc, doc_w, index_type);
545                 wrbuf_printf(wrbuf, ">");
546             }
547             if (doc_w->match)
548                 wrbuf_puts(wrbuf, "<s>");
549             /* not printing leading ws */
550             if (mark_state || !doc_w->ws || doc_w->match) 
551                 wrbuf_xmlputs(wrbuf, doc_w->term);
552             if (doc_w->match)
553                 wrbuf_puts(wrbuf, "</s>");
554         }
555         else if (mark_state == 1)
556         {
557             wrbuf_puts(wrbuf, "</snippet>\n");
558         }
559         mark_state = doc_w->mark;
560     }
561     if (mark_state == 1)
562     {
563         wrbuf_puts(wrbuf, "</snippet>\n");
564     }
565     wrbuf_printf(wrbuf, "</record>");
566 }
567
568 int zebra_get_rec_snippets(ZebraHandle zh, zint sysno,
569                            zebra_snippets *snippets)
570 {
571     int return_code = 0;
572     Record rec = rec_get(zh->reg->records, sysno);
573     if (!rec)
574     {
575         yaz_log(YLOG_WARN, "rec_get fail on sysno=" ZINT_FORMAT, sysno);
576         return_code = YAZ_BIB1_SYSTEM_ERROR_IN_PRESENTING_RECORDS;
577     }
578     else
579     {
580         const char *file_type = rec->info[recInfo_fileType];
581         void *recTypeClientData;
582         RecType rt = recType_byName(zh->reg->recTypes, zh->res,
583                                     file_type, &recTypeClientData);
584
585         if (!rt)
586             return_code = YAZ_BIB1_SYSTEM_ERROR_IN_PRESENTING_RECORDS;
587         else
588         {
589             struct ZebraRecStream stream;
590             return_code = zebra_create_record_stream(zh, &rec, &stream);
591             if (return_code == 0)
592             {
593                 extract_snippet(zh, snippets, &stream,
594                                 rt, recTypeClientData);
595
596                 stream.destroy(&stream);
597             }
598         }
599         rec_free(&rec);
600     }
601     return return_code;
602 }
603
604 static int snippet_fetch(
605     struct special_fetch_s *fi, const char *elemsetname,
606     const Odr_oid *input_format,
607     const Odr_oid **output_format,
608     WRBUF result, WRBUF addinfo)
609 {
610     ZebraHandle zh = fi->zh;
611     zebra_snippets *rec_snippets = zebra_snippets_create();
612     int return_code = zebra_get_rec_snippets(zh, fi->sysno, rec_snippets);
613
614     if (!return_code)
615     {
616         WRBUF wrbuf = result;
617         zebra_snippets *hit_snippet = zebra_snippets_create();
618
619         zebra_snippets_hit_vector(zh, fi->setname, fi->sysno, hit_snippet);
620
621 #if 0
622         /* for debugging purposes */
623         yaz_log(YLOG_LOG, "---------------------------");
624         yaz_log(YLOG_LOG, "REC SNIPPET:");
625         zebra_snippets_log(rec_snippets, YLOG_LOG, 1);
626         yaz_log(YLOG_LOG, "---------------------------");
627         yaz_log(YLOG_LOG, "HIT SNIPPET:");
628         zebra_snippets_log(hit_snippet, YLOG_LOG, 1);
629 #endif
630         
631         zebra_snippets_ring(rec_snippets, hit_snippet, 5, 5);
632         
633 #if 0
634         yaz_log(YLOG_LOG, "---------------------------");
635         yaz_log(YLOG_LOG, "RING SNIPPET:");
636         zebra_snippets_log(rec_snippets, YLOG_LOG, 1);
637 #endif
638         snippet_xml_record(zh, wrbuf, rec_snippets);
639         
640         *output_format = yaz_oid_recsyn_xml;
641         
642         zebra_snippets_destroy(hit_snippet);
643     }
644     zebra_snippets_destroy(rec_snippets);
645     return return_code;
646 }
647
648 struct term_collect {
649     const char *term;
650     int oc;
651     zint set_occur;
652 };
653
654 static zint freq_term(ZebraHandle zh, int ord, const char *term, RSET rset_set)
655 {
656     struct rset_key_control *kc = zebra_key_control_create(zh);
657     char ord_buf[IT_MAX_WORD];
658     int ord_len = key_SU_encode(ord, ord_buf);
659     char *info;
660     zint hits = 0;
661     NMEM nmem = nmem_create();
662     
663     strcpy(ord_buf + ord_len, term);
664     
665     info = dict_lookup(zh->reg->dict, ord_buf);
666     if (info)
667     {
668         ISAM_P isam_p;
669         RSET rsets[2], rset;
670         memcpy(&isam_p, info+1, sizeof(ISAM_P));
671         
672         rsets[0] = zebra_create_rset_isam(zh, nmem, kc, kc->scope, isam_p, 0);
673         rsets[1] = rset_dup(rset_set);
674         
675         rset = rset_create_and(nmem, kc, kc->scope, 2, rsets);
676
677         zebra_count_set(zh, rset, &hits, zh->approx_limit);
678
679         rset_delete(rsets[0]);
680         rset_delete(rset);
681     }
682     (*kc->dec)(kc);
683     nmem_destroy(nmem);
684     return hits;
685 }
686
687 static int term_qsort_handle(const void *a, const void *b)
688 {
689     const struct term_collect *l = a;
690     const struct term_collect *r = b;
691     if (l->set_occur < r->set_occur)
692         return 1;
693     else if (l->set_occur > r->set_occur)
694         return -1;
695     else
696     {
697         const char *lterm = l->term ? l->term : "";
698         const char *rterm = r->term ? r->term : "";
699         return strcmp(lterm, rterm);
700     }
701 }
702
703 static void term_collect_freq(ZebraHandle zh,
704                               struct term_collect *col, int no_terms_collect,
705                               int ord, RSET rset, double scale_factor)
706 {
707     int i;
708     for (i = 0; i < no_terms_collect; i++)
709     {
710         if (col[i].term)
711         {
712             if (scale_factor < 0.0)
713             {
714                 col[i].set_occur = freq_term(zh, ord, col[i].term, rset);
715             }
716             else
717                 col[i].set_occur = scale_factor * col[i].oc;
718         }
719     }
720     qsort(col, no_terms_collect, sizeof(*col), term_qsort_handle);
721 }
722
723 static struct term_collect *term_collect_create(zebra_strmap_t sm, 
724                                                 int no_terms_collect,
725                                                 NMEM nmem)
726 {
727     const char *term;
728     void *data_buf;
729     size_t data_len;
730     zebra_strmap_it it;
731     struct term_collect *col = nmem_malloc(nmem, 
732                                            sizeof *col *no_terms_collect);
733     int i;
734     for (i = 0; i < no_terms_collect; i++)
735     {
736         col[i].term = 0;
737         col[i].oc = 0;
738         col[i].set_occur = 0;
739     }
740     /* iterate over terms and collect the most frequent ones */
741     it = zebra_strmap_it_create(sm);
742     while ((term = zebra_strmap_it_next(it, &data_buf, &data_len)))
743     {
744         /* invariant:
745            col[0] has lowest oc .  col[no_terms_collect-1] has highest oc */
746         int oc = *(int*) data_buf;
747         int j = 0;
748         /* insertion may be slow but terms terms will be "infrequent" and
749            thus number of iterations should be small below 
750         */
751         while (j < no_terms_collect && oc > col[j].oc)
752             j++;
753         if (j) 
754         {   /* oc <= col[j] and oc > col[j-1] */
755             --j;
756             memmove(col, col+1, sizeof(*col) * j);
757             col[j].term = term;
758             col[j].oc = oc;
759         }
760     }
761     zebra_strmap_it_destroy(it);
762     return col;
763 }
764
765 static int perform_facet_sort(ZebraHandle zh, int no_ord, int *ord_array,
766                               zebra_strmap_t *map_array,
767                               int num_recs, ZebraMetaRecord *poset)
768 {
769     int rec_i;
770     WRBUF w = wrbuf_alloc();
771     int ord_i;
772
773     for (ord_i = 0; ord_i < no_ord; ord_i++)
774     {
775         for (rec_i = 0; rec_i < num_recs; rec_i++)
776         {
777             if (!poset[rec_i].sysno)
778                 continue;
779             
780             zebra_sort_sysno(zh->reg->sort_index, poset[rec_i].sysno);
781             zebra_sort_type(zh->reg->sort_index, ord_array[ord_i]);
782             
783             wrbuf_rewind(w);
784             if (zebra_sort_read(zh->reg->sort_index, 0, w))
785             {
786                 zebra_strmap_t sm = map_array[ord_i];
787                 int off = 0;
788                 while (off != wrbuf_len(w))
789                 {
790                     const char *str = wrbuf_buf(w) + off;
791                     int *freq = zebra_strmap_lookup(sm, str, 0, 0);
792                     if (freq)
793                         (*freq)++;
794                     else
795                     {
796                         int v = 1;
797                         zebra_strmap_add(sm, str, &v, sizeof v);
798                     }
799                     off += strlen(str)+1;
800                 }
801             }
802         }
803     }
804     wrbuf_destroy(w);
805     return 0;
806 }
807
808
809 static int perform_facet_index(ZebraHandle zh,
810                                struct special_fetch_s *fi,
811                                int no_ord, int *ord_array,
812                                zebra_strmap_t *map_array,
813                                int num_recs, ZebraMetaRecord *poset,
814                                struct index_spec *spec_list)
815 {
816     int max_chunks = 2;
817     int rec_i;
818     res_get_int(zh->res, "facetMaxChunks", &max_chunks);
819
820     for (rec_i = 0; rec_i < num_recs; rec_i++)
821     {
822         int ret;
823         int j;
824         zint sysnos[MAX_SYSNOS_PER_RECORD];
825         int no_sysnos = MAX_SYSNOS_PER_RECORD;
826         if (!poset[rec_i].sysno)
827             continue;
828         ret = zebra_result_recid_to_sysno(zh, fi->setname,
829                                           poset[rec_i].sysno,
830                                           sysnos, &no_sysnos);
831         assert(no_sysnos > 0);
832         yaz_log(YLOG_DEBUG, "Analyzing rec=%d ISAM sysno=" ZINT_FORMAT " chunks=%d",
833                 rec_i, poset[rec_i].sysno, no_sysnos);
834         for (j = 0; j < no_sysnos && j < max_chunks; j++)
835         {
836             size_t slen;
837             const char *str;
838             struct it_key key_in;
839             Record rec = rec_get(zh->reg->records, sysnos[j]);
840             zebra_rec_keys_t keys = zebra_rec_keys_open();
841             zebra_rec_keys_set_buf(keys, rec->info[recInfo_delKeys],
842                                    rec->size[recInfo_delKeys], 0);
843             
844             yaz_log(YLOG_DEBUG, "rec %d " ZINT_FORMAT " %s", 
845                     j, sysnos[j], zebra_rec_keys_empty(keys) ? "empty" : "non-empty");
846             if (zebra_rec_keys_rewind(keys))
847             {
848                 while (zebra_rec_keys_read(keys, &str, &slen, &key_in))
849                 {
850                     int ord_i;
851                     struct index_spec *spec;
852                     for (spec = spec_list, ord_i = 0; ord_i < no_ord; 
853                          ord_i++, spec = spec->next)
854                     {
855                         int ord = CAST_ZINT_TO_INT(key_in.mem[0]);
856                         if (ord == ord_array[ord_i] && 
857                             str[0] != FIRST_IN_FIELD_CHAR)
858                         {
859                             int *freq;
860                             zebra_strmap_t sm = map_array[ord_i];
861                             
862                             freq = zebra_strmap_lookup(sm, str, 0, 0);
863                             if (freq)
864                                 (*freq)++;
865                             else
866                             {
867                                 int v = 1;
868                                 zebra_strmap_add(sm, str, &v, sizeof v);
869                             }
870                         }
871                     }
872                 }
873             }
874             zebra_rec_keys_close(keys);
875             rec_free(&rec);
876         }
877     }
878     return 0;
879 }
880
881 static int perform_facet(ZebraHandle zh,  
882                          struct special_fetch_s *fi,
883                          WRBUF result,
884                          int num_recs, ZebraMetaRecord *poset,
885                          struct index_spec *spec_list,
886                          int no_ord, int *ord_array,
887                          int use_xml,
888                          zinfo_index_category_t cat)
889 {
890     int i;
891     int ret = 0;
892     WRBUF wr = result;
893     struct index_spec *spec;
894     yaz_timing_t timing = yaz_timing_create();
895     zebra_strmap_t *map_array
896         = nmem_malloc(fi->nmem, sizeof *map_array * no_ord);
897     for (i = 0; i < no_ord; i++)
898         map_array[i] = zebra_strmap_create();
899
900     if (cat == zinfo_index_category_sort)
901         perform_facet_sort(zh, no_ord, ord_array, map_array,
902                            num_recs, poset);
903     else
904         perform_facet_index(zh, fi, no_ord, ord_array, map_array,
905                             num_recs, poset, spec_list);
906     yaz_timing_stop(timing);
907     yaz_log(YLOG_LOG, "facet first phase real=%4.2f cat=%s",
908             yaz_timing_get_real(timing),
909             (cat == zinfo_index_category_sort) ? "sort" : "index");
910     yaz_timing_start(timing);
911     for (spec = spec_list, i = 0; i < no_ord; i++, spec = spec->next)
912     {
913         int j;
914         NMEM nmem = nmem_create();
915         struct term_collect *col;
916         int no_collect_terms = 20;
917         
918         if (spec->extra)
919             no_collect_terms = atoi(spec->extra);
920         if (no_collect_terms < 1)
921             no_collect_terms = 1;
922         col = term_collect_create(map_array[i], no_collect_terms, nmem);
923         term_collect_freq(zh, col, no_collect_terms, ord_array[i],
924                           resultSetRef(zh, fi->setname), 
925                           cat == zinfo_index_category_sort ? 1.0 : -1.0);
926         
927         if (use_xml)
928             wrbuf_printf(wr, "  <facet type=\"%s\" index=\"%s\">\n",
929                          spec->index_type, spec->index_name);
930         else
931             wrbuf_printf(wr, "facet %s %s\n",
932                          spec->index_type, spec->index_name);
933         for (j = 0; j < no_collect_terms; j++)
934         {
935             if (col[j].term)
936             {
937                 char dst_buf[IT_MAX_WORD];
938                 zebra_term_untrans(zh, spec->index_type, dst_buf, col[j].term);
939                 if (use_xml)
940                 {
941                     wrbuf_printf(wr, "    <term coccur=\"%d\"", col[j].oc);
942                     if (col[j].set_occur)
943                         wrbuf_printf(wr, " occur=\"" ZINT_FORMAT "\"", 
944                                      col[j].set_occur);
945                     wrbuf_printf(wr, ">");
946                     wrbuf_xmlputs(wr, dst_buf);
947                     wrbuf_printf(wr, "</term>\n");
948                 }
949                 else
950                 {
951                     wrbuf_printf(wr, "term %d", col[j].oc);
952                     if (col[j].set_occur)
953                         wrbuf_printf(wr, " " ZINT_FORMAT, 
954                                      col[j].set_occur);
955                     wrbuf_printf(wr, ": %s\n", dst_buf);
956                 }
957             }
958         }
959         if (use_xml)
960             wrbuf_puts(wr, "  </facet>\n");
961         nmem_destroy(nmem);
962     }
963     for (i = 0; i < no_ord; i++)
964         zebra_strmap_destroy(map_array[i]);
965     yaz_timing_stop(timing);
966     yaz_log(YLOG_LOG, "facet second phase real=%4.2f",
967             yaz_timing_get_real(timing));
968     yaz_timing_destroy(&timing);
969     return ret;
970 }
971
972 static int facet_fetch(
973     struct special_fetch_s *fi, const char *elemsetname,
974     const Odr_oid *input_format,
975     const Odr_oid **output_format,
976     WRBUF result, WRBUF addinfo)
977 {
978     zint *pos_array;
979     int i;
980     int num_recs = 10; /* number of records to analyze */
981     ZebraMetaRecord *poset;
982     ZEBRA_RES ret = ZEBRA_OK;
983     int *ord_array;
984     int use_xml = 0;
985     int no_ord = 0;
986     struct index_spec *spec, *spec_list;
987     int error;
988     ZebraHandle zh = fi->zh;
989     /* whether sort or index based */
990     zinfo_index_category_t cat = zinfo_index_category_sort;
991
992     /* see if XML is required for response */
993     if (oid_oidcmp(input_format, yaz_oid_recsyn_xml) == 0)
994         use_xml = 1;
995
996     spec_list = parse_index_spec(elemsetname, fi->nmem, &error);
997               
998     if (!spec_list || error)
999     {
1000         return YAZ_BIB1_SPECIFIED_ELEMENT_SET_NAME_NOT_VALID_FOR_SPECIFIED_;
1001     }          
1002   
1003     for (spec = spec_list; spec; spec = spec->next)
1004     {
1005         if (!spec->index_type)
1006             return YAZ_BIB1_SPECIFIED_ELEMENT_SET_NAME_NOT_VALID_FOR_SPECIFIED_;
1007         no_ord++;
1008     }
1009
1010     /* try to see if all specs are sort based.. If not, try the
1011        index based ones */
1012     ord_array = nmem_malloc(fi->nmem, sizeof(*ord_array) * no_ord);
1013
1014     for (spec = spec_list, i = 0; spec; spec = spec->next, i++)
1015     {
1016         int ord = zebraExplain_lookup_attr_str(zh->reg->zei,
1017                                                zinfo_index_category_sort,
1018                                                spec->index_type,
1019                                                spec->index_name);
1020         if (ord == -1)
1021             break;
1022         ord_array[i] = ord;
1023         num_recs = 10000;
1024     }
1025     if (spec)
1026     {
1027         cat = zinfo_index_category_index;
1028         for (spec = spec_list, i = 0; spec; spec = spec->next, i++)
1029         {
1030             int ord = zebraExplain_lookup_attr_str(zh->reg->zei,
1031                                                    zinfo_index_category_index,
1032                                                    spec->index_type,
1033                                                    spec->index_name);
1034             if (ord == -1)
1035                 break;
1036             ord_array[i] = ord;
1037             
1038         }
1039     }
1040     if (spec)
1041         return YAZ_BIB1_SPECIFIED_ELEMENT_SET_NAME_NOT_VALID_FOR_SPECIFIED_;
1042
1043     res_get_int(zh->res, "facetNumRecs", &num_recs);
1044
1045     pos_array = (zint *) nmem_malloc(fi->nmem, num_recs * sizeof(*pos_array));
1046     for (i = 0; i < num_recs; i++)
1047         pos_array[i] = i+1;
1048     poset = zebra_meta_records_create(zh, fi->setname, num_recs, pos_array);
1049     if (!poset)
1050     {
1051         wrbuf_puts(addinfo, fi->setname);
1052         return YAZ_BIB1_SPECIFIED_RESULT_SET_DOES_NOT_EXIST;
1053     }
1054     else
1055     {
1056         if (use_xml)
1057         {
1058             wrbuf_printf(result, ZEBRA_XML_HEADER_STR ">\n");
1059         }
1060         ret = perform_facet(zh, fi, result, num_recs, poset,
1061                             spec_list, no_ord, ord_array, use_xml,
1062                             cat);
1063         if (use_xml)
1064             wrbuf_puts(result, "</record>\n");
1065     }
1066     *output_format = yaz_oid_recsyn_xml;
1067     zebra_meta_records_destroy(zh, poset, num_recs);
1068     return ret;
1069 }
1070
1071
1072 static int zebra_special_fetch(
1073     void *handle, const char *elemsetname,
1074     const Odr_oid *input_format,
1075     const Odr_oid **output_format,
1076     WRBUF result, WRBUF addinfo)
1077 {
1078     Record rec = 0;
1079     struct special_fetch_s *fi = (struct special_fetch_s *) handle;
1080     ZebraHandle zh = fi->zh;
1081     zint sysno = fi->sysno;
1082     
1083     /* processing zebra::facet */
1084     if (elemsetname && 0 == strncmp(elemsetname, "facet", 5))
1085     {
1086         return facet_fetch(fi, elemsetname + 5, 
1087                            input_format, output_format,
1088                            result, addinfo);
1089     }
1090
1091     if (elemsetname && 0 == strcmp(elemsetname, "snippet"))
1092     {
1093         return snippet_fetch(fi, elemsetname + 7,
1094                              input_format, output_format,
1095                              result, addinfo);
1096     }
1097
1098     /* processing zebra::meta::sysno  */
1099     if (elemsetname && 0 == strcmp(elemsetname, "meta::sysno"))
1100     {
1101         int ret = 0;
1102         if (!oid_oidcmp(input_format, yaz_oid_recsyn_sutrs))
1103         {
1104             wrbuf_printf(result, ZINT_FORMAT, fi->sysno);
1105             *output_format = input_format;
1106         } 
1107         else if (!oid_oidcmp(input_format, yaz_oid_recsyn_xml))
1108         {
1109             wrbuf_printf(result, ZEBRA_XML_HEADER_STR
1110                          " sysno=\"" ZINT_FORMAT "\"/>\n",
1111                          fi->sysno);
1112             *output_format = input_format;
1113         }
1114         else
1115             ret = YAZ_BIB1_NO_SYNTAXES_AVAILABLE_FOR_THIS_REQUEST;
1116         return ret;
1117     }
1118
1119     /* processing special elementsetname zebra::index:: for sort elements */
1120     if (elemsetname && 0 == strncmp(elemsetname, "index", 5))
1121     {
1122         int ret = sort_fetch(
1123             fi, elemsetname + 5,
1124             input_format, output_format,
1125             result, addinfo);
1126         if (ret != -1)
1127             return ret;
1128         /* not a sort index so we continue to get the full record */
1129     }
1130
1131
1132     /* fetching binary record up for all other display elementsets */
1133     rec = rec_get(zh->reg->records, sysno);
1134     if (!rec)
1135     {
1136         yaz_log(YLOG_WARN, "rec_get fail on sysno=" ZINT_FORMAT, sysno);
1137         return YAZ_BIB1_SYSTEM_ERROR_IN_PRESENTING_RECORDS;
1138     }
1139
1140     /* processing special elementsetnames zebra::data */    
1141     if (elemsetname && 0 == strcmp(elemsetname, "data"))
1142     {
1143         struct ZebraRecStream stream;
1144         RecordAttr *recordAttr = rec_init_attr(zh->reg->zei, rec); 
1145         char *b;
1146
1147         zebra_create_record_stream(zh, &rec, &stream);
1148         *output_format = input_format;
1149
1150         b = nmem_malloc(fi->nmem, recordAttr->recordSize);
1151         stream.readf(&stream, b, recordAttr->recordSize);
1152         wrbuf_write(result, b, recordAttr->recordSize);
1153
1154         stream.destroy(&stream);
1155         rec_free(&rec);
1156         return 0;
1157     }
1158
1159     /* processing special elementsetnames zebra::meta:: */
1160     if (elemsetname && 0 == strcmp(elemsetname, "meta"))
1161     {
1162         int ret = 0;
1163         RecordAttr *recordAttr = rec_init_attr(zh->reg->zei, rec); 
1164
1165         if (!oid_oidcmp(input_format, yaz_oid_recsyn_xml))
1166         {
1167             *output_format = input_format;
1168             
1169             wrbuf_printf(result, ZEBRA_XML_HEADER_STR
1170                          " sysno=\"" ZINT_FORMAT "\"", sysno);
1171             retrieve_puts_attr(result, "base", rec->info[recInfo_databaseName]);
1172             retrieve_puts_attr(result, "file", rec->info[recInfo_filename]);
1173             retrieve_puts_attr(result, "type", rec->info[recInfo_fileType]);
1174             if (fi->score >= 0)
1175                 retrieve_puts_attr_int(result, "score", fi->score);
1176            
1177             wrbuf_printf(result,
1178                          " rank=\"" ZINT_FORMAT "\""
1179                          " size=\"%i\""
1180                          " set=\"zebra::%s\"/>\n",
1181                          recordAttr->staticrank,
1182                          recordAttr->recordSize,
1183                          elemsetname);
1184         }
1185         else if (!oid_oidcmp(input_format, yaz_oid_recsyn_sutrs))
1186         {
1187             *output_format = input_format;
1188             wrbuf_printf(result, "sysno " ZINT_FORMAT "\n", sysno);
1189             retrieve_puts_str(result, "base", rec->info[recInfo_databaseName]);
1190             retrieve_puts_str(result, "file", rec->info[recInfo_filename]);
1191             retrieve_puts_str(result, "type", rec->info[recInfo_fileType]);
1192             if (fi->score >= 0)
1193                 retrieve_puts_int(result, "score", fi->score);
1194
1195             wrbuf_printf(result,
1196                          "rank " ZINT_FORMAT "\n"
1197                          "size %i\n"
1198                          "set zebra::%s\n",
1199                          recordAttr->staticrank,
1200                          recordAttr->recordSize,
1201                          elemsetname);
1202         }
1203         else
1204             ret = YAZ_BIB1_NO_SYNTAXES_AVAILABLE_FOR_THIS_REQUEST;
1205
1206         rec_free(&rec);
1207         return ret;
1208     }
1209
1210     /* processing special elementsetnames zebra::index:: */
1211     if (elemsetname && 0 == strncmp(elemsetname, "index", 5))
1212     {
1213         int ret = special_index_fetch(
1214             fi, elemsetname + 5,
1215             input_format, output_format,
1216             result, addinfo, rec);
1217         rec_free(&rec);
1218         return ret;
1219     }
1220
1221     if (rec)
1222         rec_free(&rec);
1223     return YAZ_BIB1_SPECIFIED_ELEMENT_SET_NAME_NOT_VALID_FOR_SPECIFIED_;
1224 }
1225
1226 int zebra_record_fetch(ZebraHandle zh, const char *setname,
1227                        zint sysno, int score,
1228                        ODR odr,
1229                        const Odr_oid *input_format, Z_RecordComposition *comp,
1230                        const Odr_oid **output_format,
1231                        char **rec_bufp, int *rec_lenp, char **basenamep,
1232                        WRBUF addinfo_w)
1233 {
1234     Record rec;
1235     char *fname, *file_type, *basename;
1236     const char *elemsetname;
1237     struct ZebraRecStream stream;
1238     RecordAttr *recordAttr;
1239     void *clientData;
1240     int return_code = 0;
1241     zint sysnos[MAX_SYSNOS_PER_RECORD];
1242     int no_sysnos = MAX_SYSNOS_PER_RECORD;
1243     ZEBRA_RES res;
1244     struct special_fetch_s fetch_info;
1245
1246     res = zebra_result_recid_to_sysno(zh, setname, sysno, sysnos, &no_sysnos);
1247     if (res != ZEBRA_OK)
1248         return ZEBRA_FAIL;
1249
1250     sysno = sysnos[0];
1251     *basenamep = 0;
1252     elemsetname = yaz_get_esn(comp);
1253
1254     fetch_info.zh = zh;
1255     fetch_info.setname = setname;
1256     fetch_info.sysno = sysno;
1257     fetch_info.score = score;
1258     fetch_info.nmem = odr->mem;
1259
1260     /* processing zebra special elementset names of form 'zebra:: */
1261     if (elemsetname && 0 == strncmp(elemsetname, "zebra::", 7))
1262     {
1263         WRBUF result = wrbuf_alloc();
1264         int r = zebra_special_fetch(&fetch_info, elemsetname + 7,
1265                                     input_format, output_format,
1266                                     result, addinfo_w);
1267         if (r == 0)
1268         {
1269             *rec_bufp = odr_strdup(odr, wrbuf_cstr(result));
1270             *rec_lenp = wrbuf_len(result);
1271         }
1272         wrbuf_destroy(result);
1273         return r;
1274     }
1275
1276     /* processing all other element set names */
1277     rec = rec_get(zh->reg->records, sysno);
1278     if (!rec)
1279     {
1280         yaz_log(YLOG_WARN, "rec_get fail on sysno=" ZINT_FORMAT, sysno);
1281         *basenamep = 0;
1282         return YAZ_BIB1_SYSTEM_ERROR_IN_PRESENTING_RECORDS;
1283     }
1284
1285
1286     recordAttr = rec_init_attr(zh->reg->zei, rec);
1287
1288     file_type = rec->info[recInfo_fileType];
1289     fname = rec->info[recInfo_filename];
1290     basename = rec->info[recInfo_databaseName];
1291     *basenamep = (char *) odr_malloc(odr, strlen(basename)+1);
1292     strcpy(*basenamep, basename);
1293
1294     yaz_log(YLOG_DEBUG, "retrieve localno=" ZINT_FORMAT " score=%d",
1295             sysno, score);
1296
1297     return_code = zebra_create_record_stream(zh, &rec, &stream);
1298
1299     if (rec)
1300     {
1301         RecType rt;
1302         struct recRetrieveCtrl retrieveCtrl;
1303
1304         retrieveCtrl.stream = &stream;
1305         retrieveCtrl.fname = fname;
1306         retrieveCtrl.localno = sysno;
1307         retrieveCtrl.staticrank = recordAttr->staticrank;
1308         retrieveCtrl.score = score;
1309         retrieveCtrl.recordSize = recordAttr->recordSize;
1310         retrieveCtrl.odr = odr;
1311         retrieveCtrl.input_format = retrieveCtrl.output_format = input_format;
1312         retrieveCtrl.comp = comp;
1313         retrieveCtrl.encoding = zh->record_encoding;
1314         retrieveCtrl.diagnostic = 0;
1315         retrieveCtrl.addinfo = 0;
1316         retrieveCtrl.dh = zh->reg->dh;
1317         retrieveCtrl.res = zh->res;
1318         retrieveCtrl.rec_buf = 0;
1319         retrieveCtrl.rec_len = -1;
1320         retrieveCtrl.handle = &fetch_info;
1321         retrieveCtrl.special_fetch = zebra_special_fetch;
1322
1323         if (!(rt = recType_byName(zh->reg->recTypes, zh->res,
1324                                   file_type, &clientData)))
1325         {
1326             wrbuf_printf(addinfo_w, "Could not handle record type %.40s",
1327                          file_type);
1328             return_code = YAZ_BIB1_SYSTEM_ERROR_IN_PRESENTING_RECORDS;
1329         }
1330         else
1331         {
1332             (*rt->retrieve)(clientData, &retrieveCtrl);
1333             return_code = retrieveCtrl.diagnostic;
1334
1335             *output_format = retrieveCtrl.output_format;
1336             *rec_bufp = (char *) retrieveCtrl.rec_buf;
1337             *rec_lenp = retrieveCtrl.rec_len;
1338             if (retrieveCtrl.addinfo)
1339                 wrbuf_puts(addinfo_w, retrieveCtrl.addinfo);
1340         }
1341
1342         stream.destroy(&stream);
1343         rec_free(&rec);
1344     }
1345
1346     return return_code;
1347 }
1348
1349 /*
1350  * Local variables:
1351  * c-basic-offset: 4
1352  * c-file-style: "Stroustrup"
1353  * indent-tabs-mode: nil
1354  * End:
1355  * vim: shiftwidth=4 tabstop=8 expandtab
1356  */
1357