1 /* This file is part of the Zebra server.
2 Copyright (C) 1994-2011 Index Data
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
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
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
21 \brief indexes records and extract tokens for indexing and sorting
43 #include <yaz/snprintf.h>
45 static int log_level_extract = 0;
46 static int log_level_details = 0;
47 static int log_level_initialized = 0;
49 /* 1 if we use eliminitate identical delete/insert keys */
50 /* eventually this the 0-case code will be removed */
54 static void extract_flush_record_keys2(ZebraHandle zh, zint sysno,
55 zebra_rec_keys_t ins_keys,
57 zebra_rec_keys_t del_keys,
60 static void extract_flush_record_keys(ZebraHandle zh, zint sysno,
62 zebra_rec_keys_t reckeys,
66 static void zebra_init_log_level(void)
68 if (!log_level_initialized)
70 log_level_initialized = 1;
72 log_level_extract = yaz_log_module_level("extract");
73 log_level_details = yaz_log_module_level("indexdetails");
77 static WRBUF wrbuf_hex_str(const char *cstr)
80 WRBUF w = wrbuf_alloc();
81 for (i = 0; cstr[i]; i++)
83 if (cstr[i] < ' ' || cstr[i] > 126)
84 wrbuf_printf(w, "\\%02X", cstr[i] & 0xff);
86 wrbuf_putc(w, cstr[i]);
92 static void extract_flush_sort_keys(ZebraHandle zh, zint sysno,
93 int cmd, zebra_rec_keys_t skp);
94 static void extract_schema_add(struct recExtractCtrl *p, Odr_oid *oid);
95 static void extract_token_add(RecWord *p);
97 static void check_log_limit(ZebraHandle zh)
99 if (zh->records_processed + zh->records_skipped == zh->m_file_verbose_limit)
101 yaz_log(YLOG_LOG, "More than %d file log entries. Omitting rest",
102 zh->m_file_verbose_limit);
106 static void logRecord(ZebraHandle zh)
109 ++zh->records_processed;
110 if (!(zh->records_processed % 1000))
112 yaz_log(YLOG_LOG, "Records: "ZINT_FORMAT" i/u/d "
113 ZINT_FORMAT"/"ZINT_FORMAT"/"ZINT_FORMAT,
114 zh->records_processed, zh->records_inserted,
115 zh->records_updated, zh->records_deleted);
119 static void init_extractCtrl(ZebraHandle zh, struct recExtractCtrl *ctrl)
121 ctrl->flagShowRecords = !zh->m_flag_rw;
125 static void extract_add_index_string(RecWord *p,
126 zinfo_index_category_t cat,
127 const char *str, int length);
129 static void extract_set_store_data_prepare(struct recExtractCtrl *p);
131 static void extract_init(struct recExtractCtrl *p, RecWord *w)
134 w->index_name = "any";
142 struct snip_rec_info {
144 zebra_snippets *snippets;
148 static void snippet_add_complete_field(RecWord *p, int ord,
151 struct snip_rec_info *h = p->extractCtrl->handle;
152 if (p->term_len && p->term_buf && zebra_maps_is_index(zm))
153 zebra_snippets_appendn(h->snippets, p->seqno, 0, ord,
154 p->term_buf, p->term_len);
158 static void snippet_add_incomplete_field(RecWord *p, int ord, zebra_map_t zm)
160 struct snip_rec_info *h = p->extractCtrl->handle;
161 const char *b = p->term_buf;
162 int remain = p->term_len;
164 const char **map = 0;
165 const char *start = b;
166 const char *last = b;
169 map = zebra_maps_input(zm, &b, remain, 0);
176 while (map && *map && **map == *CHR_SPACE)
178 remain = p->term_len - (b - p->term_buf);
181 map = zebra_maps_input(zm, &b, remain, 0);
187 if (start != last && zebra_maps_is_index(zm))
189 zebra_snippets_appendn(h->snippets, p->seqno, 1, ord,
190 start, last - start);
193 while (map && *map && **map != *CHR_SPACE)
195 remain = p->term_len - (b - p->term_buf);
198 map = zebra_maps_input(zm, &b, remain, 0);
208 if (zebra_maps_is_first_in_field(zm))
210 /* first in field marker */
214 if (start != last && zebra_maps_is_index(zm))
215 zebra_snippets_appendn(h->snippets, p->seqno, 0, ord,
216 start, last - start);
223 static void snippet_add_icu(RecWord *p, int ord, zebra_map_t zm)
225 struct snip_rec_info *h = p->extractCtrl->handle;
227 const char *res_buf = 0;
230 const char *display_buf = 0;
231 size_t display_len = 0;
233 zebra_map_tokenize_start(zm, p->term_buf, p->term_len);
234 while (zebra_map_tokenize_next(zm, &res_buf, &res_len,
235 &display_buf, &display_len))
237 if (zebra_maps_is_index(zm))
238 zebra_snippets_appendn(h->snippets, p->seqno, 0, ord,
239 display_buf, display_len);
244 static void snippet_token_add(RecWord *p)
246 struct snip_rec_info *h = p->extractCtrl->handle;
247 ZebraHandle zh = h->zh;
248 zebra_map_t zm = zebra_map_get_or_add(zh->reg->zebra_maps, p->index_type);
252 ZebraExplainInfo zei = zh->reg->zei;
253 int ch = zebraExplain_lookup_attr_str(
254 zei, zinfo_index_category_index, p->index_type, p->index_name);
256 if (zebra_maps_is_icu(zm))
257 snippet_add_icu(p, ch, zm);
260 if (zebra_maps_is_complete(zm))
261 snippet_add_complete_field(p, ch, zm);
263 snippet_add_incomplete_field(p, ch, zm);
268 static void snippet_schema_add(
269 struct recExtractCtrl *p, Odr_oid *oid)
274 void extract_snippet(ZebraHandle zh, zebra_snippets *sn,
275 struct ZebraRecStream *stream,
276 RecType rt, void *recTypeClientData)
278 struct recExtractCtrl extractCtrl;
279 struct snip_rec_info info;
281 extractCtrl.stream = stream;
282 extractCtrl.first_record = 1;
283 extractCtrl.init = extract_init;
284 extractCtrl.tokenAdd = snippet_token_add;
285 extractCtrl.schemaAdd = snippet_schema_add;
289 extractCtrl.dh = zh->reg->dh;
293 extractCtrl.handle = &info;
294 extractCtrl.match_criteria[0] = '\0';
295 extractCtrl.staticrank = 0;
296 extractCtrl.action = action_insert;
298 init_extractCtrl(zh, &extractCtrl);
300 extractCtrl.setStoreData = 0;
302 (*rt->extract)(recTypeClientData, &extractCtrl);
305 static void searchRecordKey(ZebraHandle zh,
306 zebra_rec_keys_t reckeys,
307 const char *index_name,
308 const char **ws, int ws_length)
312 zinfo_index_category_t cat = zinfo_index_category_index;
314 for (i = 0; i<ws_length; i++)
318 ch = zebraExplain_lookup_attr_str(zh->reg->zei, cat, "0", index_name);
320 ch = zebraExplain_lookup_attr_str(zh->reg->zei, cat, "p", index_name);
322 ch = zebraExplain_lookup_attr_str(zh->reg->zei, cat, "w", index_name);
327 if (zebra_rec_keys_rewind(reckeys))
334 while (zebra_rec_keys_read(reckeys, &str, &slen, &key))
336 assert(key.len <= IT_KEY_LEVEL_MAX && key.len > 2);
338 seqno = key.mem[key.len-1];
340 if (key.mem[0] == ch)
346 woff = seqno - startSeq;
347 if (woff >= 0 && woff < ws_length)
354 #define FILE_MATCH_BLANK "\t "
356 static char *get_match_from_spec(ZebraHandle zh,
357 zebra_rec_keys_t reckeys,
358 const char *fname, const char *spec)
360 static char dstBuf[2048]; /* static here ??? */
362 const char *s = spec;
366 for (; *s && strchr(FILE_MATCH_BLANK, *s); s++)
373 char attset_str[64], attname_str[64];
377 for (s++; strchr(FILE_MATCH_BLANK, *s); s++)
379 for (i = 0; *s && *s != ',' && *s != ')' &&
380 !strchr(FILE_MATCH_BLANK, *s); s++)
381 if (i+1 < sizeof(attset_str))
382 attset_str[i++] = *s;
383 attset_str[i] = '\0';
385 for (; strchr(FILE_MATCH_BLANK, *s); s++)
388 strcpy(attname_str, attset_str);
391 for (s++; strchr(FILE_MATCH_BLANK, *s); s++)
393 for (i = 0; *s && *s != ')' &&
394 !strchr(FILE_MATCH_BLANK, *s); s++)
395 if (i+1 < sizeof(attname_str))
396 attname_str[i++] = *s;
397 attname_str[i] = '\0';
401 yaz_log(YLOG_WARN, "Missing ) in match criteria %s in group %s",
402 spec, zh->m_group ? zh->m_group : "none");
407 searchRecordKey(zh, reckeys, attname_str, ws, 32);
408 if (0) /* for debugging */
410 for (i = 0; i<32; i++)
414 WRBUF w = wrbuf_hex_str(ws[i]);
415 yaz_log(YLOG_LOG, "ws[%d] = %s", i, wrbuf_cstr(w));
421 for (i = 0; i<32; i++)
430 dst += strlen(ws[i]);
434 yaz_log(YLOG_WARN, "Record didn't contain match"
435 " fields in (%s,%s)", attset_str, attname_str);
443 const char *spec_src = NULL;
444 const char *s1 = ++s;
445 while (*s1 && !strchr(FILE_MATCH_BLANK, *s1))
449 if (spec_len > sizeof(special)-1)
450 spec_len = sizeof(special)-1;
451 memcpy(special, s, spec_len);
452 special[spec_len] = '\0';
455 if (!strcmp(special, "group"))
456 spec_src = zh->m_group;
457 else if (!strcmp(special, "database"))
458 spec_src = zh->basenames[0];
459 else if (!strcmp(special, "filename")) {
462 else if (!strcmp(special, "type"))
463 spec_src = zh->m_record_type;
468 strcpy(dst, spec_src);
469 dst += strlen(spec_src);
472 else if (*s == '\"' || *s == '\'')
474 int stopMarker = *s++;
478 while (*s && *s != stopMarker)
480 if (i+1 < sizeof(tmpString))
481 tmpString[i++] = *s++;
486 strcpy(dst, tmpString);
487 dst += strlen(tmpString);
491 yaz_log(YLOG_WARN, "Syntax error in match criteria %s in group %s",
492 spec, zh->m_group ? zh->m_group : "none");
499 yaz_log(YLOG_WARN, "No match criteria for record %s in group %s",
500 fname, zh->m_group ? zh->m_group : "none");
505 if (0) /* for debugging */
507 WRBUF w = wrbuf_hex_str(dstBuf);
508 yaz_log(YLOG_LOG, "get_match_from_spec %s", wrbuf_cstr(w));
515 struct recordLogInfo {
518 struct recordGroup *rGroup;
521 /** \brief add the always-matches index entry and map to real record ID
522 \param ctrl record control
523 \param record_id custom record ID
524 \param sysno system record ID
526 This function serves two purposes.. It adds the always matches
527 entry and makes a pointer from the custom record ID (if defined)
528 back to the system record ID (sysno)
529 See zebra_recid_to_sysno .
531 static void all_matches_add(struct recExtractCtrl *ctrl, zint record_id,
535 extract_init(ctrl, &word);
536 word.record_id = record_id;
537 /* we use the seqno as placeholder for a way to get back to
538 record database from _ALLRECORDS.. This is used if a custom
539 RECORD was defined */
541 word.index_name = "_ALLRECORDS";
542 word.index_type = "w";
544 extract_add_index_string(&word, zinfo_index_category_alwaysmatches,
548 /* forward declaration */
549 ZEBRA_RES zebra_extract_records_stream(ZebraHandle zh,
550 struct ZebraRecStream *stream,
551 enum zebra_recctrl_action_t action,
552 const char *recordType,
554 const char *match_criteria,
557 void *recTypeClientData);
560 ZEBRA_RES zebra_extract_file(ZebraHandle zh, zint *sysno, const char *fname,
561 enum zebra_recctrl_action_t action)
563 ZEBRA_RES r = ZEBRA_OK;
568 const char *original_record_type = 0;
570 void *recTypeClientData;
571 struct ZebraRecStream stream, *streamp;
573 zebra_init_log_level();
575 if (!zh->m_group || !*zh->m_group)
578 sprintf(gprefix, "%s.", zh->m_group);
580 yaz_log(log_level_extract, "zebra_extract_file %s", fname);
582 /* determine file extension */
584 for (i = strlen(fname); --i >= 0; )
587 else if (fname[i] == '.')
589 strcpy(ext, fname+i+1);
592 /* determine file type - depending on extension */
593 original_record_type = zh->m_record_type;
594 if (!zh->m_record_type)
596 sprintf(ext_res, "%srecordType.%s", gprefix, ext);
597 zh->m_record_type = res_get(zh->res, ext_res);
599 if (!zh->m_record_type)
602 if (zh->records_processed + zh->records_skipped
603 < zh->m_file_verbose_limit)
604 yaz_log(YLOG_LOG, "? %s", fname);
605 zh->records_skipped++;
608 /* determine match criteria */
609 if (!zh->m_record_id)
611 sprintf(ext_res, "%srecordId.%s", gprefix, ext);
612 zh->m_record_id = res_get(zh->res, ext_res);
616 recType_byName(zh->reg->recTypes, zh->res, zh->m_record_type,
617 &recTypeClientData)))
619 yaz_log(YLOG_WARN, "No such record type: %s", zh->m_record_type);
623 switch(recType->version)
628 yaz_log(YLOG_WARN, "Bad filter version: %s", zh->m_record_type);
630 if (sysno && (action == action_delete || action == action_a_delete))
638 if (zh->path_reg && !yaz_is_abspath(fname))
640 strcpy(full_rep, zh->path_reg);
641 strcat(full_rep, "/");
642 strcat(full_rep, fname);
645 strcpy(full_rep, fname);
647 if ((fd = open(full_rep, O_BINARY|O_RDONLY)) == -1)
649 yaz_log(YLOG_WARN|YLOG_ERRNO, "open %s", full_rep);
650 zh->m_record_type = original_record_type;
654 zebra_create_stream_fd(streamp, fd, 0);
656 r = zebra_extract_records_stream(zh, streamp,
660 0, /*match_criteria */
662 recType, recTypeClientData);
664 stream.destroy(streamp);
665 zh->m_record_type = original_record_type;
670 If sysno is provided, then it's used to identify the reocord.
671 If not, and match_criteria is provided, then sysno is guessed
672 If not, and a record is provided, then sysno is got from there
676 ZEBRA_RES zebra_buffer_extract_record(ZebraHandle zh,
677 const char *buf, size_t buf_size,
678 enum zebra_recctrl_action_t action,
679 const char *recordType,
681 const char *match_criteria,
684 struct ZebraRecStream stream;
689 if (recordType && *recordType)
691 yaz_log(log_level_extract,
692 "Record type explicitly specified: %s", recordType);
693 recType = recType_byName(zh->reg->recTypes, zh->res, recordType,
698 if (!(zh->m_record_type))
700 yaz_log(YLOG_WARN, "No such record type defined");
703 yaz_log(log_level_extract, "Get record type from rgroup: %s",
705 recType = recType_byName(zh->reg->recTypes, zh->res,
706 zh->m_record_type, &clientData);
707 recordType = zh->m_record_type;
712 yaz_log(YLOG_WARN, "No such record type: %s", recordType);
716 zebra_create_stream_mem(&stream, buf, buf_size);
718 res = zebra_extract_records_stream(zh, &stream,
724 recType, clientData);
725 stream.destroy(&stream);
729 static ZEBRA_RES zebra_extract_record_stream(ZebraHandle zh,
730 struct ZebraRecStream *stream,
731 enum zebra_recctrl_action_t action,
732 const char *recordType,
734 const char *match_criteria,
737 void *recTypeClientData,
742 RecordAttr *recordAttr;
743 struct recExtractCtrl extractCtrl;
745 const char *matchStr = 0;
747 off_t start_offset = 0, end_offset = 0;
748 const char *pr_fname = fname; /* filename to print .. */
749 int show_progress = zh->records_processed + zh->records_skipped
750 < zh->m_file_verbose_limit ? 1:0;
752 zebra_init_log_level();
755 pr_fname = "<no file>"; /* make it printable if file is omitted */
757 zebra_rec_keys_reset(zh->reg->keys);
758 zebra_rec_keys_reset(zh->reg->sortKeys);
760 if (zebraExplain_curDatabase(zh->reg->zei, zh->basenames[0]))
762 if (zebraExplain_newDatabase(zh->reg->zei, zh->basenames[0],
763 zh->m_explain_database))
769 off_t null_offset = 0;
770 extractCtrl.stream = stream;
772 start_offset = stream->tellf(stream);
774 extractCtrl.first_record = start_offset ? 0 : 1;
776 stream->endf(stream, &null_offset);;
778 extractCtrl.init = extract_init;
779 extractCtrl.tokenAdd = extract_token_add;
780 extractCtrl.schemaAdd = extract_schema_add;
781 extractCtrl.dh = zh->reg->dh;
782 extractCtrl.handle = zh;
783 extractCtrl.match_criteria[0] = '\0';
784 extractCtrl.staticrank = 0;
785 extractCtrl.action = action;
787 init_extractCtrl(zh, &extractCtrl);
789 extract_set_store_data_prepare(&extractCtrl);
791 r = (*recType->extract)(recTypeClientData, &extractCtrl);
793 if (action == action_update)
795 action = extractCtrl.action;
800 case RECCTRL_EXTRACT_EOF:
802 case RECCTRL_EXTRACT_ERROR_GENERIC:
803 /* error occured during extraction ... */
804 yaz_log(YLOG_WARN, "extract error: generic");
806 case RECCTRL_EXTRACT_ERROR_NO_SUCH_FILTER:
807 /* error occured during extraction ... */
808 yaz_log(YLOG_WARN, "extract error: no such filter");
810 case RECCTRL_EXTRACT_SKIP:
812 yaz_log(YLOG_LOG, "skip %s %s " ZINT_FORMAT,
813 recordType, pr_fname, (zint) start_offset);
816 end_offset = stream->endf(stream, 0);
818 stream->seekf(stream, end_offset);
821 case RECCTRL_EXTRACT_OK:
824 yaz_log(YLOG_WARN, "extract error: unknown error: %d", r);
827 end_offset = stream->endf(stream, 0);
829 stream->seekf(stream, end_offset);
831 end_offset = stream->tellf(stream);
833 if (extractCtrl.match_criteria[0])
834 match_criteria = extractCtrl.match_criteria;
839 if (zh->m_flag_rw == 0)
841 yaz_log(YLOG_LOG, "test %s %s " ZINT_FORMAT, recordType,
842 pr_fname, (zint) start_offset);
843 /* test mode .. Do not perform match */
851 if (match_criteria && *match_criteria)
852 matchStr = match_criteria;
855 if (zh->m_record_id && *zh->m_record_id)
857 matchStr = get_match_from_spec(zh, zh->reg->keys, pr_fname,
861 yaz_log(YLOG_LOG, "error %s %s " ZINT_FORMAT, recordType,
862 pr_fname, (zint) start_offset);
867 WRBUF w = wrbuf_alloc();
869 for (i = 0; i < strlen(matchStr); i++)
871 wrbuf_printf(w, "%02X", matchStr[i] & 0xff);
873 yaz_log(YLOG_LOG, "Got match %s", wrbuf_cstr(w));
880 int db_ord = zebraExplain_get_database_ord(zh->reg->zei);
881 char *rinfo = dict_lookup_ord(zh->reg->matchDict, db_ord,
885 if (log_level_extract)
887 WRBUF w = wrbuf_hex_str(matchStr);
888 yaz_log(log_level_extract, "matchStr: %s", wrbuf_cstr(w));
893 assert(*rinfo == sizeof(*sysno));
894 memcpy(sysno, rinfo+1, sizeof(*sysno));
901 /* new record AKA does not exist already */
902 if (action == action_delete)
904 yaz_log(YLOG_LOG, "delete %s %s " ZINT_FORMAT, recordType,
905 pr_fname, (zint) start_offset);
906 yaz_log(YLOG_WARN, "cannot delete record above (seems new)");
909 else if (action == action_a_delete)
912 yaz_log(YLOG_LOG, "adelete %s %s " ZINT_FORMAT, recordType,
913 pr_fname, (zint) start_offset);
916 else if (action == action_replace)
918 yaz_log(YLOG_LOG, "update %s %s " ZINT_FORMAT, recordType,
919 pr_fname, (zint) start_offset);
920 yaz_log(YLOG_WARN, "cannot update record above (seems new)");
924 yaz_log(YLOG_LOG, "add %s %s " ZINT_FORMAT, recordType, pr_fname,
925 (zint) start_offset);
926 rec = rec_new(zh->reg->records);
933 all_matches_add(&extractCtrl,
934 zebra_rec_keys_get_custom_record_id(zh->reg->keys),
939 recordAttr = rec_init_attr(zh->reg->zei, rec);
940 if (extractCtrl.staticrank < 0)
942 yaz_log(YLOG_WARN, "Negative staticrank for record. Set to 0");
943 extractCtrl.staticrank = 0;
948 int db_ord = zebraExplain_get_database_ord(zh->reg->zei);
949 dict_insert_ord(zh->reg->matchDict, db_ord, matchStr,
950 sizeof(*sysno), sysno);
953 extract_flush_sort_keys(zh, *sysno, 1, zh->reg->sortKeys);
955 extract_flush_record_keys2(zh, *sysno,
956 zh->reg->keys, extractCtrl.staticrank,
957 0, recordAttr->staticrank);
959 extract_flush_record_keys(zh, *sysno, 1, zh->reg->keys,
960 extractCtrl.staticrank);
962 recordAttr->staticrank = extractCtrl.staticrank;
963 zh->records_inserted++;
967 /* record already exists */
968 zebra_rec_keys_t delkeys = zebra_rec_keys_open();
969 zebra_rec_keys_t sortKeys = zebra_rec_keys_open();
970 if (action == action_insert)
972 yaz_log(YLOG_LOG, "skipped %s %s " ZINT_FORMAT,
973 recordType, pr_fname, (zint) start_offset);
978 rec = rec_get(zh->reg->records, *sysno);
983 all_matches_add(&extractCtrl,
984 zebra_rec_keys_get_custom_record_id(zh->reg->keys),
988 recordAttr = rec_init_attr(zh->reg->zei, rec);
990 /* decrease total size */
991 zebraExplain_recordBytesIncrement(zh->reg->zei,
992 - recordAttr->recordSize);
994 zebra_rec_keys_set_buf(delkeys,
995 rec->info[recInfo_delKeys],
996 rec->size[recInfo_delKeys],
998 zebra_rec_keys_set_buf(sortKeys,
999 rec->info[recInfo_sortKeys],
1000 rec->size[recInfo_sortKeys],
1003 extract_flush_sort_keys(zh, *sysno, 0, sortKeys);
1005 extract_flush_record_keys(zh, *sysno, 0, delkeys,
1006 recordAttr->staticrank);
1008 if (action == action_delete || action == action_a_delete)
1010 /* record going to be deleted */
1012 extract_flush_record_keys2(zh, *sysno, 0, recordAttr->staticrank,
1013 delkeys, recordAttr->staticrank);
1015 if (zebra_rec_keys_empty(delkeys))
1017 yaz_log(YLOG_LOG, "delete %s %s " ZINT_FORMAT, recordType,
1018 pr_fname, (zint) start_offset);
1019 yaz_log(YLOG_WARN, "cannot delete file above, "
1020 "storeKeys false (3)");
1025 yaz_log(YLOG_LOG, "delete %s %s " ZINT_FORMAT, recordType,
1026 pr_fname, (zint) start_offset);
1027 zh->records_deleted++;
1030 int db_ord = zebraExplain_get_database_ord(zh->reg->zei);
1031 dict_delete_ord(zh->reg->matchDict, db_ord, matchStr);
1033 rec_del(zh->reg->records, &rec);
1035 zebra_rec_keys_close(delkeys);
1036 zebra_rec_keys_close(sortKeys);
1042 { /* update or special_update */
1044 yaz_log(YLOG_LOG, "update %s %s " ZINT_FORMAT, recordType,
1045 pr_fname, (zint) start_offset);
1046 extract_flush_sort_keys(zh, *sysno, 1, zh->reg->sortKeys);
1049 extract_flush_record_keys2(zh, *sysno,
1050 zh->reg->keys, extractCtrl.staticrank,
1051 delkeys, recordAttr->staticrank);
1053 extract_flush_record_keys(zh, *sysno, 1,
1054 zh->reg->keys, extractCtrl.staticrank);
1056 recordAttr->staticrank = extractCtrl.staticrank;
1057 zh->records_updated++;
1059 zebra_rec_keys_close(delkeys);
1060 zebra_rec_keys_close(sortKeys);
1062 /* update file type */
1063 xfree(rec->info[recInfo_fileType]);
1064 rec->info[recInfo_fileType] =
1065 rec_strdup(recordType, &rec->size[recInfo_fileType]);
1067 /* update filename */
1068 xfree(rec->info[recInfo_filename]);
1069 rec->info[recInfo_filename] =
1070 rec_strdup(fname, &rec->size[recInfo_filename]);
1072 /* update delete keys */
1073 xfree(rec->info[recInfo_delKeys]);
1074 if (!zebra_rec_keys_empty(zh->reg->keys) && zh->m_store_keys == 1)
1076 zebra_rec_keys_get_buf(zh->reg->keys,
1077 &rec->info[recInfo_delKeys],
1078 &rec->size[recInfo_delKeys]);
1082 rec->info[recInfo_delKeys] = NULL;
1083 rec->size[recInfo_delKeys] = 0;
1085 /* update sort keys */
1086 xfree(rec->info[recInfo_sortKeys]);
1088 zebra_rec_keys_get_buf(zh->reg->sortKeys,
1089 &rec->info[recInfo_sortKeys],
1090 &rec->size[recInfo_sortKeys]);
1094 recordAttr->recordSize = end_offset - start_offset;
1095 zebraExplain_recordBytesIncrement(zh->reg->zei,
1096 recordAttr->recordSize);
1099 /* set run-number for this record */
1100 recordAttr->runNumber =
1101 zebraExplain_runNumberIncrement(zh->reg->zei, 0);
1103 /* update store data */
1104 xfree(rec->info[recInfo_storeData]);
1106 /* update store data */
1107 if (zh->store_data_buf)
1109 rec->size[recInfo_storeData] = zh->store_data_size;
1110 rec->info[recInfo_storeData] = zh->store_data_buf;
1111 zh->store_data_buf = 0;
1112 recordAttr->recordSize = zh->store_data_size;
1114 else if (zh->m_store_data)
1116 off_t cur_offset = stream->tellf(stream);
1118 rec->size[recInfo_storeData] = recordAttr->recordSize;
1119 rec->info[recInfo_storeData] = (char *)
1120 xmalloc(recordAttr->recordSize);
1121 stream->seekf(stream, start_offset);
1122 stream->readf(stream, rec->info[recInfo_storeData],
1123 recordAttr->recordSize);
1124 stream->seekf(stream, cur_offset);
1128 rec->info[recInfo_storeData] = NULL;
1129 rec->size[recInfo_storeData] = 0;
1131 /* update database name */
1132 xfree(rec->info[recInfo_databaseName]);
1133 rec->info[recInfo_databaseName] =
1134 rec_strdup(zh->basenames[0], &rec->size[recInfo_databaseName]);
1137 recordAttr->recordOffset = start_offset;
1139 /* commit this record */
1140 rec_put(zh->reg->records, &rec);
1145 /** \brief extracts records from stream
1146 \param zh Zebra Handle
1147 \param stream stream that we read from
1148 \param action (action_insert, action_replace, action_delete, ..)
1149 \param recordType Record filter type "grs.xml", etc.
1150 \param sysno pointer to sysno if already known; NULL otherwise
1151 \param match_criteria (NULL if not already given)
1152 \param fname filename that we read from (for logging purposes only)
1153 \param recType record type
1154 \param recTypeClientData client data for record type
1155 \returns ZEBRA_OK for success; ZEBRA_FAIL for failure
1157 ZEBRA_RES zebra_extract_records_stream(ZebraHandle zh,
1158 struct ZebraRecStream *stream,
1159 enum zebra_recctrl_action_t action,
1160 const char *recordType,
1162 const char *match_criteria,
1165 void *recTypeClientData)
1167 ZEBRA_RES res = ZEBRA_OK;
1171 res = zebra_extract_record_stream(zh, stream,
1177 recType, recTypeClientData, &more);
1183 if (res != ZEBRA_OK)
1191 ZEBRA_RES zebra_extract_explain(void *handle, Record rec, data1_node *n)
1193 ZebraHandle zh = (ZebraHandle) handle;
1194 struct recExtractCtrl extractCtrl;
1196 if (zebraExplain_curDatabase(zh->reg->zei,
1197 rec->info[recInfo_databaseName]))
1200 if (zebraExplain_newDatabase(zh->reg->zei,
1201 rec->info[recInfo_databaseName], 0))
1205 zebra_rec_keys_reset(zh->reg->keys);
1206 zebra_rec_keys_reset(zh->reg->sortKeys);
1208 extractCtrl.init = extract_init;
1209 extractCtrl.tokenAdd = extract_token_add;
1210 extractCtrl.schemaAdd = extract_schema_add;
1211 extractCtrl.dh = zh->reg->dh;
1213 init_extractCtrl(zh, &extractCtrl);
1215 extractCtrl.flagShowRecords = 0;
1216 extractCtrl.match_criteria[0] = '\0';
1217 extractCtrl.staticrank = 0;
1218 extractCtrl.action = action_update;
1220 extractCtrl.handle = handle;
1221 extractCtrl.first_record = 1;
1223 extract_set_store_data_prepare(&extractCtrl);
1226 grs_extract_tree(&extractCtrl, n);
1228 if (rec->size[recInfo_delKeys])
1230 zebra_rec_keys_t delkeys = zebra_rec_keys_open();
1232 zebra_rec_keys_t sortkeys = zebra_rec_keys_open();
1234 zebra_rec_keys_set_buf(delkeys, rec->info[recInfo_delKeys],
1235 rec->size[recInfo_delKeys],
1238 extract_flush_record_keys2(zh, rec->sysno,
1239 zh->reg->keys, 0, delkeys, 0);
1241 extract_flush_record_keys(zh, rec->sysno, 0, delkeys, 0);
1242 extract_flush_record_keys(zh, rec->sysno, 1, zh->reg->keys, 0);
1244 zebra_rec_keys_close(delkeys);
1246 zebra_rec_keys_set_buf(sortkeys, rec->info[recInfo_sortKeys],
1247 rec->size[recInfo_sortKeys],
1250 extract_flush_sort_keys(zh, rec->sysno, 0, sortkeys);
1251 zebra_rec_keys_close(sortkeys);
1256 extract_flush_record_keys2(zh, rec->sysno, zh->reg->keys, 0, 0, 0);
1258 extract_flush_record_keys(zh, rec->sysno, 1, zh->reg->keys, 0);
1261 extract_flush_sort_keys(zh, rec->sysno, 1, zh->reg->sortKeys);
1263 xfree(rec->info[recInfo_delKeys]);
1264 zebra_rec_keys_get_buf(zh->reg->keys,
1265 &rec->info[recInfo_delKeys],
1266 &rec->size[recInfo_delKeys]);
1268 xfree(rec->info[recInfo_sortKeys]);
1269 zebra_rec_keys_get_buf(zh->reg->sortKeys,
1270 &rec->info[recInfo_sortKeys],
1271 &rec->size[recInfo_sortKeys]);
1275 void zebra_it_key_str_dump(ZebraHandle zh, struct it_key *key,
1276 const char *str, size_t slen, NMEM nmem, int level)
1278 char keystr[200]; /* room for zints to print */
1280 int ord = CAST_ZINT_TO_INT(key->mem[0]);
1281 const char *index_type;
1283 const char *string_index;
1285 zebraExplain_lookup_ord(zh->reg->zei, ord, &index_type,
1286 0/* db */, &string_index);
1288 zebra_term_untrans_iconv(zh, nmem, index_type,
1291 for (i = 0; i < key->len; i++)
1293 sprintf(keystr + strlen(keystr), ZINT_FORMAT " ", key->mem[i]);
1296 if (*str < CHR_BASE_CHAR)
1299 char dst_buf[200]; /* room for special chars */
1301 strcpy(dst_buf , "?");
1303 if (!strcmp(str, ""))
1304 strcpy(dst_buf, "alwaysmatches");
1305 if (!strcmp(str, FIRST_IN_FIELD_STR))
1306 strcpy(dst_buf, "firstinfield");
1307 else if (!strcmp(str, CHR_UNKNOWN))
1308 strcpy(dst_buf, "unknown");
1309 else if (!strcmp(str, CHR_SPACE))
1310 strcpy(dst_buf, "space");
1312 for (i = 0; i<slen; i++)
1314 sprintf(dst_buf + strlen(dst_buf), " %d", str[i] & 0xff);
1316 yaz_log(level, "%s%s %s %s", keystr, index_type,
1317 string_index, dst_buf);
1321 yaz_log(level, "%s%s %s \"%s\"", keystr, index_type,
1322 string_index, dst_term);
1325 void extract_rec_keys_log(ZebraHandle zh, int is_insert,
1326 zebra_rec_keys_t reckeys,
1329 if (zebra_rec_keys_rewind(reckeys))
1334 NMEM nmem = nmem_create();
1336 while(zebra_rec_keys_read(reckeys, &str, &slen, &key))
1338 zebra_it_key_str_dump(zh, &key, str, slen, nmem, level);
1345 void extract_rec_keys_adjust(ZebraHandle zh, int is_insert,
1346 zebra_rec_keys_t reckeys)
1348 ZebraExplainInfo zei = zh->reg->zei;
1352 struct ord_stat *next;
1355 if (zebra_rec_keys_rewind(reckeys))
1357 struct ord_stat *ord_list = 0;
1361 struct it_key key_in;
1362 while(zebra_rec_keys_read(reckeys, &str, &slen, &key_in))
1364 int ord = CAST_ZINT_TO_INT(key_in.mem[0]);
1366 for (p = ord_list; p ; p = p->next)
1374 p = xmalloc(sizeof(*p));
1385 struct ord_stat *p1 = p;
1388 zebraExplain_ord_adjust_occurrences(zei, p->ord, p->no, 1);
1390 zebraExplain_ord_adjust_occurrences(zei, p->ord, - p->no, -1);
1398 static void extract_flush_record_keys2(
1399 ZebraHandle zh, zint sysno,
1400 zebra_rec_keys_t ins_keys, zint ins_rank,
1401 zebra_rec_keys_t del_keys, zint del_rank)
1403 ZebraExplainInfo zei = zh->reg->zei;
1407 if (!zh->reg->key_block)
1409 int mem = 1024*1024 * atoi( res_get_def( zh->res, "memmax", "8"));
1410 const char *key_tmp_dir = res_get_def(zh->res, "keyTmpDir", ".");
1411 int use_threads = atoi(res_get_def(zh->res, "threads", "1"));
1412 zh->reg->key_block = key_block_create(mem, key_tmp_dir, use_threads);
1417 extract_rec_keys_adjust(zh, 1, ins_keys);
1419 zebraExplain_recordCountIncrement(zei, 1);
1420 zebra_rec_keys_rewind(ins_keys);
1424 extract_rec_keys_adjust(zh, 0, del_keys);
1426 zebraExplain_recordCountIncrement(zei, -1);
1427 zebra_rec_keys_rewind(del_keys);
1433 const char *del_str;
1434 struct it_key del_key_in;
1438 const char *ins_str;
1439 struct it_key ins_key_in;
1443 del = zebra_rec_keys_read(del_keys, &del_str, &del_slen,
1446 ins = zebra_rec_keys_read(ins_keys, &ins_str, &ins_slen,
1449 if (del && ins && ins_rank == del_rank
1450 && !key_compare(&del_key_in, &ins_key_in)
1451 && ins_slen == del_slen && !memcmp(del_str, ins_str, del_slen))
1461 key_block_write(zh->reg->key_block, sysno,
1462 &del_key_in, 0, del_str, del_slen,
1463 del_rank, zh->m_staticrank);
1465 key_block_write(zh->reg->key_block, sysno,
1466 &ins_key_in, 1, ins_str, ins_slen,
1467 ins_rank, zh->m_staticrank);
1469 yaz_log(log_level_extract, "normal=%d optimized=%d", normal, optimized);
1472 static void extract_flush_record_keys(
1473 ZebraHandle zh, zint sysno, int cmd,
1474 zebra_rec_keys_t reckeys,
1477 ZebraExplainInfo zei = zh->reg->zei;
1479 extract_rec_keys_adjust(zh, cmd, reckeys);
1481 if (log_level_details)
1483 yaz_log(log_level_details, "Keys for record " ZINT_FORMAT " %s",
1484 sysno, cmd ? "insert" : "delete");
1485 extract_rec_keys_log(zh, cmd, reckeys, log_level_details);
1488 if (!zh->reg->key_block)
1490 int mem = 1024*1024 * atoi( res_get_def( zh->res, "memmax", "8"));
1491 const char *key_tmp_dir = res_get_def(zh->res, "keyTmpDir", ".");
1492 int use_threads = atoi(res_get_def(zh->res, "threads", "1"));
1493 zh->reg->key_block = key_block_create(mem, key_tmp_dir, use_threads);
1495 zebraExplain_recordCountIncrement(zei, cmd ? 1 : -1);
1498 yaz_log(YLOG_LOG, "sysno=" ZINT_FORMAT " cmd=%d", sysno, cmd);
1499 print_rec_keys(zh, reckeys);
1501 if (zebra_rec_keys_rewind(reckeys))
1505 struct it_key key_in;
1506 while(zebra_rec_keys_read(reckeys, &str, &slen, &key_in))
1508 key_block_write(zh->reg->key_block, sysno,
1509 &key_in, cmd, str, slen,
1510 staticrank, zh->m_staticrank);
1516 ZEBRA_RES zebra_rec_keys_to_snippets1(ZebraHandle zh,
1517 zebra_rec_keys_t reckeys,
1518 zebra_snippets *snippets)
1520 NMEM nmem = nmem_create();
1521 if (zebra_rec_keys_rewind(reckeys))
1526 while (zebra_rec_keys_read(reckeys, &str, &slen, &key))
1531 const char *index_type;
1533 assert(key.len <= IT_KEY_LEVEL_MAX && key.len > 2);
1534 seqno = key.mem[key.len-1];
1535 ord = CAST_ZINT_TO_INT(key.mem[0]);
1537 zebraExplain_lookup_ord(zh->reg->zei, ord, &index_type,
1538 0/* db */, 0 /* string_index */);
1540 zebra_term_untrans_iconv(zh, nmem, index_type,
1542 zebra_snippets_append(snippets, seqno, 0, ord, dst_term);
1550 void print_rec_keys(ZebraHandle zh, zebra_rec_keys_t reckeys)
1552 yaz_log(YLOG_LOG, "print_rec_keys");
1553 if (zebra_rec_keys_rewind(reckeys))
1558 while (zebra_rec_keys_read(reckeys, &str, &slen, &key))
1560 char dst_buf[IT_MAX_WORD];
1562 const char *index_type;
1563 int ord = CAST_ZINT_TO_INT(key.mem[0]);
1565 assert(key.len <= IT_KEY_LEVEL_MAX && key.len > 2);
1567 zebraExplain_lookup_ord(zh->reg->zei, ord, &index_type, &db, 0);
1569 seqno = key.mem[key.len-1];
1571 zebra_term_untrans(zh, index_type, dst_buf, str);
1573 yaz_log(YLOG_LOG, "ord=%d seqno=" ZINT_FORMAT
1574 " term=%s", ord, seqno, dst_buf);
1579 static void extract_add_index_string(RecWord *p, zinfo_index_category_t cat,
1580 const char *str, int length)
1583 ZebraHandle zh = p->extractCtrl->handle;
1584 ZebraExplainInfo zei = zh->reg->zei;
1587 ch = zebraExplain_lookup_attr_str(zei, cat, p->index_type, p->index_name);
1589 ch = zebraExplain_add_attr_str(zei, cat, p->index_type, p->index_name);
1593 key.mem[i++] = p->record_id;
1594 key.mem[i++] = p->section_id;
1596 if (zh->m_segment_indexing)
1597 key.mem[i++] = p->segment;
1598 key.mem[i++] = p->seqno;
1601 zebra_rec_keys_write(zh->reg->keys, str, length, &key);
1604 static void extract_add_sort_string(RecWord *p, const char *str, int length)
1607 ZebraHandle zh = p->extractCtrl->handle;
1608 ZebraExplainInfo zei = zh->reg->zei;
1610 zinfo_index_category_t cat = zinfo_index_category_sort;
1612 ch = zebraExplain_lookup_attr_str(zei, cat, p->index_type, p->index_name);
1614 ch = zebraExplain_add_attr_str(zei, cat, p->index_type, p->index_name);
1617 key.mem[1] = p->record_id;
1618 key.mem[2] = p->section_id;
1620 zebra_rec_keys_write(zh->reg->sortKeys, str, length, &key);
1623 static void extract_add_staticrank_string(RecWord *p,
1624 const char *str, int length)
1627 struct recExtractCtrl *ctrl = p->extractCtrl;
1629 if (length > sizeof(valz)-1)
1630 length = sizeof(valz)-1;
1632 memcpy(valz, str, length);
1633 valz[length] = '\0';
1634 ctrl->staticrank = atozint(valz);
1637 static void extract_add_string(RecWord *p, zebra_map_t zm,
1638 const char *string, int length)
1644 if (log_level_details)
1647 WRBUF w = wrbuf_alloc();
1649 wrbuf_write_escaped(w, string, length);
1650 yaz_log(log_level_details, "extract_add_string: %s", wrbuf_cstr(w));
1653 if (zebra_maps_is_index(zm))
1655 extract_add_index_string(p, zinfo_index_category_index,
1657 if (zebra_maps_is_alwaysmatches(zm))
1660 memcpy(&word, p, sizeof(word));
1663 extract_add_index_string(
1664 &word, zinfo_index_category_alwaysmatches, "", 0);
1667 else if (zebra_maps_is_sort(zm))
1669 extract_add_sort_string(p, string, length);
1671 else if (zebra_maps_is_staticrank(zm))
1673 extract_add_staticrank_string(p, string, length);
1677 static void extract_add_incomplete_field(RecWord *p, zebra_map_t zm)
1679 const char *b = p->term_buf;
1680 int remain = p->term_len;
1682 const char **map = 0;
1685 map = zebra_maps_input(zm, &b, remain, 0);
1689 char buf[IT_MAX_WORD+1];
1693 while (map && *map && **map == *CHR_SPACE)
1695 remain = p->term_len - (b - p->term_buf);
1697 map = zebra_maps_input(zm, &b, remain, 0);
1704 while (map && *map && **map != *CHR_SPACE)
1706 const char *cp = *map;
1708 while (i < IT_MAX_WORD && *cp)
1710 remain = p->term_len - (b - p->term_buf);
1712 map = zebra_maps_input(zm, &b, remain, 0);
1722 if (zebra_maps_is_first_in_field(zm))
1724 /* first in field marker */
1725 extract_add_string(p, zm, FIRST_IN_FIELD_STR, FIRST_IN_FIELD_LEN);
1729 extract_add_string(p, zm, buf, i);
1734 static void extract_add_complete_field(RecWord *p, zebra_map_t zm)
1736 const char *b = p->term_buf;
1737 char buf[IT_MAX_WORD+1];
1738 const char **map = 0;
1739 int i = 0, remain = p->term_len;
1742 map = zebra_maps_input(zm, &b, remain, 1);
1744 while (remain > 0 && i < IT_MAX_WORD)
1746 while (map && *map && **map == *CHR_SPACE)
1748 remain = p->term_len - (b - p->term_buf);
1752 int first = i ? 0 : 1; /* first position */
1753 map = zebra_maps_input(zm, &b, remain, first);
1761 if (i && i < IT_MAX_WORD)
1762 buf[i++] = *CHR_SPACE;
1763 while (map && *map && **map != *CHR_SPACE)
1765 const char *cp = *map;
1767 if (**map == *CHR_CUT)
1773 if (i >= IT_MAX_WORD)
1775 while (i < IT_MAX_WORD && *cp)
1778 remain = p->term_len - (b - p->term_buf);
1781 map = zebra_maps_input(zm, &b, remain, 0);
1789 extract_add_string(p, zm, buf, i);
1793 static void extract_add_icu(RecWord *p, zebra_map_t zm)
1795 const char *res_buf = 0;
1798 zebra_map_tokenize_start(zm, p->term_buf, p->term_len);
1799 while (zebra_map_tokenize_next(zm, &res_buf, &res_len, 0, 0))
1801 extract_add_string(p, zm, res_buf, res_len);
1807 /** \brief top-level indexing handler for recctrl system
1808 \param p token data to be indexed
1812 extract_add_{in}_complete / extract_add_icu
1815 extract_add_index_string
1817 extract_add_sort_string
1819 extract_add_staticrank_string
1822 static void extract_token_add(RecWord *p)
1824 ZebraHandle zh = p->extractCtrl->handle;
1825 zebra_map_t zm = zebra_map_get_or_add(zh->reg->zebra_maps, p->index_type);
1828 if (log_level_details)
1830 yaz_log(log_level_details, "extract_token_add "
1831 "type=%s index=%s seqno=" ZINT_FORMAT " s=%.*s",
1832 p->index_type, p->index_name,
1833 p->seqno, p->term_len, p->term_buf);
1835 if ((wrbuf = zebra_replace(zm, 0, p->term_buf, p->term_len)))
1837 p->term_buf = wrbuf_buf(wrbuf);
1838 p->term_len = wrbuf_len(wrbuf);
1840 if (zebra_maps_is_icu(zm))
1842 extract_add_icu(p, zm);
1846 if (zebra_maps_is_complete(zm))
1847 extract_add_complete_field(p, zm);
1849 extract_add_incomplete_field(p, zm);
1853 static void extract_set_store_data_cb(struct recExtractCtrl *p,
1854 void *buf, size_t sz)
1856 ZebraHandle zh = (ZebraHandle) p->handle;
1858 xfree(zh->store_data_buf);
1859 zh->store_data_buf = 0;
1860 zh->store_data_size = 0;
1863 zh->store_data_buf = xmalloc(sz);
1864 zh->store_data_size = sz;
1865 memcpy(zh->store_data_buf, buf, sz);
1869 static void extract_set_store_data_prepare(struct recExtractCtrl *p)
1871 ZebraHandle zh = (ZebraHandle) p->handle;
1872 xfree(zh->store_data_buf);
1873 zh->store_data_buf = 0;
1874 zh->store_data_size = 0;
1875 p->setStoreData = extract_set_store_data_cb;
1878 static void extract_schema_add(struct recExtractCtrl *p, Odr_oid *oid)
1880 ZebraHandle zh = (ZebraHandle) p->handle;
1881 zebraExplain_addSchema(zh->reg->zei, oid);
1884 void extract_flush_sort_keys(ZebraHandle zh, zint sysno,
1885 int cmd, zebra_rec_keys_t reckeys)
1888 yaz_log(YLOG_LOG, "extract_flush_sort_keys cmd=%d sysno=" ZINT_FORMAT,
1890 extract_rec_keys_log(zh, cmd, reckeys, YLOG_LOG);
1893 if (zebra_rec_keys_rewind(reckeys))
1895 zebra_sort_index_t si = zh->reg->sort_index;
1898 struct it_key key_in;
1900 NMEM nmem = nmem_create();
1901 struct sort_add_ent {
1904 struct sort_add_ent *next;
1909 struct sort_add_ent *sort_ent_list = 0;
1911 while (zebra_rec_keys_read(reckeys, &str, &slen, &key_in))
1913 int ord = CAST_ZINT_TO_INT(key_in.mem[0]);
1914 zint filter_sysno = key_in.mem[1];
1915 zint section_id = key_in.mem[2];
1917 struct sort_add_ent **e = &sort_ent_list;
1918 for (; *e; e = &(*e)->next)
1919 if ((*e)->ord == ord && section_id == (*e)->section_id)
1923 *e = nmem_malloc(nmem, sizeof(**e));
1925 (*e)->wrbuf = wrbuf_alloc();
1928 (*e)->sysno = filter_sysno ? filter_sysno : sysno;
1929 (*e)->section_id = section_id;
1932 wrbuf_write((*e)->wrbuf, str, slen);
1933 wrbuf_putc((*e)->wrbuf, '\0');
1937 zint last_sysno = 0;
1938 struct sort_add_ent *e = sort_ent_list;
1939 for (; e; e = e->next)
1941 if (last_sysno != e->sysno)
1943 zebra_sort_sysno(si, e->sysno);
1944 last_sysno = e->sysno;
1946 zebra_sort_type(si, e->ord);
1948 zebra_sort_add(si, e->section_id, e->wrbuf);
1950 zebra_sort_delete(si, e->section_id);
1951 wrbuf_destroy(e->wrbuf);
1961 * c-file-style: "Stroustrup"
1962 * indent-tabs-mode: nil
1964 * vim: shiftwidth=4 tabstop=8 expandtab