zebra_end_trans is just calling zebra_end_transaction now: this second one is re
[idzebra-moved-to-github.git] / index / zebraapi.c
1 /* $Id: zebraapi.c,v 1.86 2003-02-27 23:08:10 pop 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
25 #include <assert.h>
26 #include <stdio.h>
27 #ifdef WIN32
28 #include <io.h>
29 #include <process.h>
30 #include <direct.h>
31 #else
32 #include <unistd.h>
33 #endif
34
35 #include <yaz/diagbib1.h>
36 #include "index.h"
37 #include <charmap.h>
38
39 /* simple asserts to validate the most essential input args */
40 #define ASSERTZH assert(zh && zh->service)
41 #define ASSERTZHRES assert(zh && zh->service && zh->res)
42 #define ASSERTZS assert(zs)
43
44 static Res zebra_open_res (ZebraHandle zh);
45 static void zebra_close_res (ZebraHandle zh);
46
47
48 static void zebra_chdir (ZebraService zs)
49 {
50     const char *dir ;
51     ASSERTZS;
52     dir = res_get (zs->global_res, "chdir");
53     if (!dir)
54         return;
55     logf (LOG_DEBUG, "chdir %s", dir);
56 #ifdef WIN32
57     _chdir(dir);
58 #else
59     chdir (dir);
60 #endif
61 }
62
63 static void zebra_flush_reg (ZebraHandle zh)
64 {
65     ASSERTZH;
66     zh->errCode=0;
67     zebraExplain_flush (zh->reg->zei, zh);
68     
69     extract_flushWriteKeys (zh);
70     zebra_index_merge (zh);
71 }
72
73 static struct zebra_register *zebra_register_open (ZebraService zs, 
74                                                    const char *name,
75                                                    int rw, int useshadow,
76                                                    Res res,
77                                                    const char *reg_path);
78 static void zebra_register_close (ZebraService zs, struct zebra_register *reg);
79
80 ZebraHandle zebra_open (ZebraService zs)
81 {
82     ZebraHandle zh;
83     const char *default_encoding;
84     ASSERTZS;
85
86     if (!zs)
87         return 0;
88
89     zh = (ZebraHandle) xmalloc (sizeof(*zh));
90     yaz_log (LOG_DEBUG, "zebra_open zs=%p returns %p", zs, zh);
91
92     zh->service = zs;
93     zh->reg = 0;          /* no register attached yet */
94     zh->sets = 0;
95     zh->destroyed = 0;
96     zh->errCode = 0;
97     zh->errString = 0;
98     zh->res = 0; 
99
100     zh->reg_name = xstrdup ("");
101     zh->path_reg = 0;
102     zh->num_basenames = 0;
103     zh->basenames = 0;
104
105     zh->trans_no = 0;
106
107     zh->lock_normal = 0;
108     zh->lock_shadow = 0;
109
110     zh->admin_databaseName = 0;
111
112     zh->shadow_enable = 1;
113
114     default_encoding = res_get_def(zs->global_res, "encoding", "ISO-8859-1");
115     zh->record_encoding = xstrdup (default_encoding);
116
117     zh->iconv_to_utf8 =
118         yaz_iconv_open ("UTF-8", default_encoding);
119     if (zh->iconv_to_utf8 == 0)
120         yaz_log (LOG_WARN, "iconv: %s to UTF-8 unsupported",
121            default_encoding);
122     zh->iconv_from_utf8 =
123         yaz_iconv_open (default_encoding, "UTF-8");
124     if (zh->iconv_to_utf8 == 0)
125         yaz_log (LOG_WARN, "iconv: UTF-8 to %s unsupported",
126            default_encoding);
127
128     zebra_mutex_cond_lock (&zs->session_lock);
129
130     zh->next = zs->sessions;
131     zs->sessions = zh;
132
133     zebra_mutex_cond_unlock (&zs->session_lock);
134
135     return zh;
136 }
137
138 ZebraService zebra_start (const char *configName)
139 {
140     Res res;
141
142     yaz_log (LOG_LOG, "zebra_start %s", configName);
143
144     if ((res = res_open (configName, 0)))
145     {
146         ZebraService zh = xmalloc (sizeof(*zh));
147
148         yaz_log (LOG_DEBUG, "Read resources `%s'", configName);
149         
150         zh->global_res = res;
151         zh->configName = xstrdup(configName);
152         zh->sessions = 0;
153         
154         zebra_chdir (zh);
155         
156         zebra_mutex_cond_init (&zh->session_lock);
157         if (!res_get (zh->global_res, "passwd"))
158             zh->passwd_db = NULL;
159         else
160         {
161             zh->passwd_db = passwd_db_open ();
162             if (!zh->passwd_db)
163                 logf (LOG_WARN|LOG_ERRNO, "passwd_db_open failed");
164             else
165                 passwd_db_file (zh->passwd_db,
166                                 res_get (zh->global_res, "passwd"));
167         }
168         zh->path_root = res_get (zh->global_res, "root");
169         return zh;
170     }
171     return 0;
172 }
173
174 static
175 struct zebra_register *zebra_register_open (ZebraService zs, const char *name,
176                                             int rw, int useshadow, Res res,
177                                             const char *reg_path)
178 {
179     struct zebra_register *reg;
180     int record_compression = REC_COMPRESS_NONE;
181     char *recordCompression = 0;
182
183     ASSERTZS;
184     
185     reg = xmalloc (sizeof(*reg));
186
187     assert (name);
188     reg->name = xstrdup (name);
189
190     reg->seqno = 0;
191     reg->last_val = 0;
192
193     assert (res);
194
195     yaz_log (LOG_DEBUG, "zebra_register_open rw = %d useshadow=%d p=%p",
196              rw, useshadow, reg);
197
198     reg->dh = data1_createx (DATA1_FLAG_XML);
199     if (!reg->dh)
200         return 0;
201     reg->bfs = bfs_create (res_get (res, "register"), reg_path);
202     if (!reg->bfs)
203     {
204         data1_destroy(reg->dh);
205         return 0;
206     }
207     if (useshadow)
208         bf_cache (reg->bfs, res_get (res, "shadow"));
209     data1_set_tabpath (reg->dh, res_get_def(res, "profilePath",
210                                             DEFAULT_PROFILE_PATH));
211     data1_set_tabroot (reg->dh, reg_path);
212     reg->recTypes = recTypes_init (reg->dh);
213     recTypes_default_handlers (reg->recTypes);
214
215     reg->zebra_maps = zebra_maps_open (res, reg_path);
216     reg->rank_classes = NULL;
217
218     reg->key_buf = 0;
219
220     reg->keys.buf_max = 0;
221     reg->keys.buf = 0;
222     reg->sortKeys.buf = 0;
223     reg->sortKeys.buf_max = 0;
224
225     reg->records = 0;
226     reg->dict = 0;
227     reg->sortIdx = 0;
228     reg->isams = 0;
229     reg->matchDict = 0;
230     reg->isam = 0;
231     reg->isamc = 0;
232     reg->isamd = 0;
233     reg->isamb = 0;
234     reg->zei = 0;
235     reg->matchDict = 0;
236     reg->key_file_no = 0;
237     
238     zebraRankInstall (reg, rank1_class);
239     zebraRankInstall (reg, rankzv_class);
240
241     recordCompression = res_get_def (res, "recordCompression", "none");
242     if (!strcmp (recordCompression, "none"))
243         record_compression = REC_COMPRESS_NONE;
244     if (!strcmp (recordCompression, "bzip2"))
245         record_compression = REC_COMPRESS_BZIP2;
246
247     if (!(reg->records = rec_open (reg->bfs, rw, record_compression)))
248     {
249         logf (LOG_WARN, "rec_open");
250         return 0;
251     }
252     if (rw)
253     {
254         reg->matchDict = dict_open (reg->bfs, GMATCH_DICT, 20, 1, 0);
255     }
256     if (!(reg->dict = dict_open (reg->bfs, FNAME_DICT, 40, rw, 0)))
257     {
258         logf (LOG_WARN, "dict_open");
259         return 0;
260     }
261     if (!(reg->sortIdx = sortIdx_open (reg->bfs, rw)))
262     {
263         logf (LOG_WARN, "sortIdx_open");
264         return 0;
265     }
266     if (res_get_match (res, "isam", "s", ISAM_DEFAULT))
267     {
268         struct ISAMS_M_s isams_m;
269         if (!(reg->isams = isams_open (reg->bfs, FNAME_ISAMS, rw,
270                                       key_isams_m(res, &isams_m))))
271         {
272             logf (LOG_WARN, "isams_open");
273             return 0;
274         }
275     }
276     if (res_get_match (res, "isam", "i", ISAM_DEFAULT))
277     {
278         if (!(reg->isam = is_open (reg->bfs, FNAME_ISAM, key_compare, rw,
279                                   sizeof (struct it_key), res)))
280         {
281             logf (LOG_WARN, "is_open");
282             return 0;
283         }
284     }
285     if (res_get_match (res, "isam", "c", ISAM_DEFAULT))
286     {
287         struct ISAMC_M_s isamc_m;
288         if (!(reg->isamc = isc_open (reg->bfs, FNAME_ISAMC,
289                                     rw, key_isamc_m(res, &isamc_m))))
290         {
291             logf (LOG_WARN, "isc_open");
292             return 0;
293         }
294     }
295     if (res_get_match (res, "isam", "d", ISAM_DEFAULT))
296     {
297         struct ISAMD_M_s isamd_m;
298         
299         if (!(reg->isamd = isamd_open (reg->bfs, FNAME_ISAMD,
300                                       rw, key_isamd_m(res, &isamd_m))))
301         {
302             logf (LOG_WARN, "isamd_open");
303             return 0;
304         }
305     }
306     if (res_get_match (res, "isam", "b", ISAM_DEFAULT))
307     {
308         struct ISAMC_M_s isamc_m;
309         
310         if (!(reg->isamb = isamb_open (reg->bfs, "isamb",
311                                        rw, key_isamc_m(res, &isamc_m), 0)))
312         {
313             logf (LOG_WARN, "isamb_open");
314             return 0;
315         }
316     }
317     if (res_get_match (res, "isam", "bc", ISAM_DEFAULT))
318     {
319         struct ISAMC_M_s isamc_m;
320         
321         if (!(reg->isamb = isamb_open (reg->bfs, "isamb",
322                                        rw, key_isamc_m(res, &isamc_m), 1)))
323         {
324             logf (LOG_WARN, "isamb_open");
325             return 0;
326         }
327     }
328     if (res_get_match (res, "isam", "null", ISAM_DEFAULT))
329     {
330         struct ISAMC_M_s isamc_m;
331         
332         if (!(reg->isamb = isamb_open (reg->bfs, "isamb",
333                                        rw, key_isamc_m(res, &isamc_m), -1)))
334         {
335             logf (LOG_WARN, "isamb_open");
336             return 0;
337         }
338     }
339     reg->zei = zebraExplain_open (reg->records, reg->dh,
340                                   res, rw, reg,
341                                   explain_extract);
342     if (!reg->zei)
343     {
344         logf (LOG_WARN, "Cannot obtain EXPLAIN information");
345         return 0;
346     }
347     reg->active = 2;
348     yaz_log (LOG_DEBUG, "zebra_register_open ok p=%p", reg);
349     return reg;
350 }
351
352 void zebra_admin_shutdown (ZebraHandle zh)
353 {
354     ASSERTZH;
355     zh->errCode=0;
356
357     zebra_mutex_cond_lock (&zh->service->session_lock);
358     zh->service->stop_flag = 1;
359     zebra_mutex_cond_unlock (&zh->service->session_lock);
360 }
361
362 void zebra_admin_start (ZebraHandle zh)
363 {
364     ZebraService zs;
365     ASSERTZH;
366     zh->errCode=0;
367     zs = zh->service;
368     zebra_mutex_cond_lock (&zs->session_lock);
369     zebra_mutex_cond_unlock (&zs->session_lock);
370 }
371
372 static void zebra_register_close (ZebraService zs, struct zebra_register *reg)
373 {
374     ASSERTZS;
375     yaz_log(LOG_DEBUG, "zebra_register_close p=%p", reg);
376     reg->stop_flag = 0;
377     zebra_chdir (zs);
378     if (reg->records)
379     {
380         zebraExplain_close (reg->zei);
381         dict_close (reg->dict);
382         if (reg->matchDict)
383             dict_close (reg->matchDict);
384         sortIdx_close (reg->sortIdx);
385         if (reg->isams)
386             isams_close (reg->isams);
387         if (reg->isam)
388             is_close (reg->isam);
389         if (reg->isamc)
390             isc_close (reg->isamc);
391         if (reg->isamd)
392             isamd_close (reg->isamd);
393         if (reg->isamb)
394             isamb_close (reg->isamb);
395         rec_close (&reg->records);
396     }
397
398     recTypes_destroy (reg->recTypes);
399     zebra_maps_close (reg->zebra_maps);
400     zebraRankDestroy (reg);
401     bfs_destroy (reg->bfs);
402     data1_destroy (reg->dh);
403
404     xfree (reg->sortKeys.buf);
405     xfree (reg->keys.buf);
406
407     xfree (reg->key_buf);
408     xfree (reg->name);
409     xfree (reg);
410 }
411
412 void zebra_stop(ZebraService zs)
413 {
414     if (!zs)
415         return ;
416     yaz_log (LOG_LOG, "zebra_stop");
417
418     while (zs->sessions)
419     {
420         zebra_close (zs->sessions);
421     }
422         
423     zebra_mutex_cond_destroy (&zs->session_lock);
424
425     if (zs->passwd_db)
426         passwd_db_close (zs->passwd_db);
427
428     res_close (zs->global_res);
429     xfree (zs->configName);
430     xfree (zs->path_root);
431     xfree (zs);
432 }
433
434 void zebra_close (ZebraHandle zh)
435 {
436     ZebraService zs;
437     struct zebra_session **sp;
438     int i;
439
440     if (!zh)
441         return;
442     ASSERTZH;
443     zh->errCode=0;
444     
445     zs = zh->service;
446     yaz_log (LOG_DEBUG, "zebra_close zh=%p", zh);
447     if (!zh)
448         return ;
449     resultSetDestroy (zh, -1, 0, 0);
450
451     if (zh->reg)
452         zebra_register_close (zh->service, zh->reg);
453     zebra_close_res (zh);
454
455     xfree (zh->record_encoding);
456
457     for (i = 0; i < zh->num_basenames; i++)
458         xfree (zh->basenames[i]);
459     xfree (zh->basenames);
460
461     if (zh->iconv_to_utf8 != 0)
462         yaz_iconv_close (zh->iconv_to_utf8);
463     if (zh->iconv_from_utf8 != 0)
464         yaz_iconv_close (zh->iconv_from_utf8);
465
466     xfree (zh->admin_databaseName);
467     zebra_mutex_cond_lock (&zs->session_lock);
468     zebra_lock_destroy (zh->lock_normal);
469     zebra_lock_destroy (zh->lock_shadow);
470     sp = &zs->sessions;
471     while (1)
472     {
473         assert (*sp);
474         if (*sp == zh)
475         {
476             *sp = (*sp)->next;
477             break;
478         }
479         sp = &(*sp)->next;
480     }
481     zebra_mutex_cond_unlock (&zs->session_lock);
482     xfree (zh->reg_name);
483     zh->service=0; /* more likely to trigger an assert */
484     xfree (zh);
485 }
486
487 struct map_baseinfo {
488     ZebraHandle zh;
489     NMEM mem;
490     int num_bases;
491     char **basenames;
492     int new_num_bases;
493     char **new_basenames;
494     int new_num_max;
495 };
496
497 static Res zebra_open_res (ZebraHandle zh)
498 {
499     Res res = 0;
500     char fname[512];
501     ASSERTZH;
502     zh->errCode=0;
503
504     if (zh->path_reg)
505     {
506         sprintf (fname, "%.200s/zebra.cfg", zh->path_reg);
507         res = res_open (fname, zh->service->global_res);
508         if (!res)
509             res = zh->service->global_res;
510     }
511     else if (*zh->reg_name == 0)
512     {
513         res = zh->service->global_res;
514     }
515     else
516     {
517         yaz_log (LOG_WARN, "no register root specified");
518         return 0;  /* no path for register - fail! */
519     }
520     return res;
521 }
522
523 static void zebra_close_res (ZebraHandle zh)
524 {
525     ASSERTZH;
526     zh->errCode=0;
527     if (zh->res != zh->service->global_res)
528         res_close (zh->res);
529     zh->res = 0;
530 }
531
532 static int zebra_select_register (ZebraHandle zh, const char *new_reg)
533 {
534     ASSERTZH;
535     zh->errCode=0;
536     if (zh->res && strcmp (zh->reg_name, new_reg) == 0)
537         return 0;
538     if (!zh->res)
539     {
540         assert (zh->reg == 0);
541         assert (*zh->reg_name == 0);
542     }
543     else
544     {
545         if (zh->reg)
546         {
547             resultSetInvalidate (zh);
548             zebra_register_close (zh->service, zh->reg);
549             zh->reg = 0;
550         }
551         zebra_close_res(zh);
552     }
553     xfree (zh->reg_name);
554     zh->reg_name = xstrdup (new_reg);
555
556     xfree (zh->path_reg);
557     zh->path_reg = 0;
558     if (zh->service->path_root)
559     {
560         zh->path_reg = xmalloc (strlen(zh->service->path_root) + 
561                                 strlen(zh->reg_name) + 3);
562         strcpy (zh->path_reg, zh->service->path_root);
563         if (*zh->reg_name)
564         {
565             strcat (zh->path_reg, "/");
566             strcat (zh->path_reg, zh->reg_name);
567         }
568     }
569     zh->res = zebra_open_res (zh);
570     
571     if (zh->lock_normal)
572         zebra_lock_destroy (zh->lock_normal);
573     zh->lock_normal = 0;
574
575     if (zh->lock_shadow)
576         zebra_lock_destroy (zh->lock_shadow);
577     zh->lock_shadow = 0;
578
579     if (zh->res)
580     {
581         char fname[512];
582         const char *lock_area  =res_get (zh->res, "lockDir");
583         
584         if (!lock_area && zh->path_reg)
585             res_put (zh->res, "lockDir", zh->path_reg);
586         sprintf (fname, "norm.%s.LCK", zh->reg_name);
587         zh->lock_normal =
588             zebra_lock_create (res_get(zh->res, "lockDir"), fname, 0);
589         
590         sprintf (fname, "shadow.%s.LCK", zh->reg_name);
591         zh->lock_shadow =
592             zebra_lock_create (res_get(zh->res, "lockDir"), fname, 0);
593
594     }
595     return 1;
596 }
597
598 void map_basenames_func (void *vp, const char *name, const char *value)
599 {
600     struct map_baseinfo *p = (struct map_baseinfo *) vp;
601     int i, no;
602     char fromdb[128], todb[8][128];
603     
604     no =
605         sscanf (value, "%127s %127s %127s %127s %127s %127s %127s %127s %127s",
606                 fromdb, todb[0], todb[1], todb[2], todb[3], todb[4],
607                 todb[5], todb[6], todb[7]);
608     if (no < 2)
609         return ;
610     no--;
611     for (i = 0; i<p->num_bases; i++)
612         if (p->basenames[i] && !STRCASECMP (p->basenames[i], fromdb))
613         {
614             p->basenames[i] = 0;
615             for (i = 0; i < no; i++)
616             {
617                 if (p->new_num_bases == p->new_num_max)
618                     return;
619                 p->new_basenames[(p->new_num_bases)++] = 
620                     nmem_strdup (p->mem, todb[i]);
621             }
622             return;
623         }
624 }
625
626 void map_basenames (ZebraHandle zh, ODR stream,
627                     int *num_bases, char ***basenames)
628 {
629     struct map_baseinfo info;
630     struct map_baseinfo *p = &info;
631     int i;
632     ASSERTZH;
633     zh->errCode=0;
634
635     info.zh = zh;
636     info.num_bases = *num_bases;
637     info.basenames = *basenames;
638     info.new_num_max = 128;
639     info.new_num_bases = 0;
640     info.new_basenames = (char **)
641         odr_malloc (stream, sizeof(*info.new_basenames) * info.new_num_max);
642     info.mem = stream->mem;
643
644     res_trav (zh->service->global_res, "mapdb", &info, map_basenames_func);
645     
646     for (i = 0; i<p->num_bases; i++)
647         if (p->basenames[i] && p->new_num_bases < p->new_num_max)
648         {
649             p->new_basenames[(p->new_num_bases)++] = 
650                 nmem_strdup (p->mem, p->basenames[i]);
651         }
652     *num_bases = info.new_num_bases;
653     *basenames = info.new_basenames;
654     for (i = 0; i<*num_bases; i++)
655         logf (LOG_LOG, "base %s", (*basenames)[i]);
656 }
657
658 int zebra_select_database (ZebraHandle zh, const char *basename)
659 {
660     ASSERTZH;
661     zh->errCode=0;
662     return zebra_select_databases (zh, 1, &basename);
663 }
664
665 int zebra_select_databases (ZebraHandle zh, int num_bases,
666                             const char **basenames)
667 {
668     int i;
669     const char *cp;
670     int len = 0;
671     char *new_reg = 0;
672     ASSERTZH;
673     zh->errCode=0;
674     
675     if (num_bases < 1)
676     {
677         zh->errCode = 23;
678         return -1;
679     }
680     for (i = 0; i < zh->num_basenames; i++)
681         xfree (zh->basenames[i]);
682     xfree (zh->basenames);
683     
684     zh->num_basenames = num_bases;
685     zh->basenames = xmalloc (zh->num_basenames * sizeof(*zh->basenames));
686     for (i = 0; i < zh->num_basenames; i++)
687         zh->basenames[i] = xstrdup (basenames[i]);
688
689     cp = strrchr(basenames[0], '/');
690     if (cp)
691     {
692         len = cp - basenames[0];
693         new_reg = xmalloc (len + 1);
694         memcpy (new_reg, basenames[0], len);
695         new_reg[len] = '\0';
696     }
697     else
698         new_reg = xstrdup ("");
699     for (i = 1; i<num_bases; i++)
700     {
701         const char *cp1;
702
703         cp1 = strrchr (basenames[i], '/');
704         if (cp)
705         {
706             if (!cp1)
707             {
708                 zh->errCode = 23;
709                 return -1;
710             }
711             if (len != cp1 - basenames[i] ||
712                 memcmp (basenames[i], new_reg, len))
713             {
714                 zh->errCode = 23;
715                 return -1;
716             }
717         }
718         else
719         {
720             if (cp1)
721             {
722                 zh->errCode = 23;
723                 return -1;
724             }
725         }
726     }
727     zebra_select_register (zh, new_reg);
728     xfree (new_reg);
729     if (!zh->res)
730     {
731         zh->errCode = 109;
732         return -1;
733     }
734     if (!zh->lock_normal || !zh->lock_shadow)
735     {
736         zh->errCode = 2;
737         return -1;
738     }
739     return 0;
740 }
741
742 void zebra_search_rpn (ZebraHandle zh, ODR decode, ODR stream,
743                        Z_RPNQuery *query, const char *setname, int *hits)
744 {
745     ASSERTZH;
746     zh->errCode=0;
747     zh->hits = 0;
748     *hits = 0;
749
750     if (zebra_begin_read (zh))
751         return;
752     resultSetAddRPN (zh, decode, stream, query, 
753                      zh->num_basenames, zh->basenames, setname);
754
755     zebra_end_read (zh);
756
757     *hits = zh->hits;
758 }
759
760 void zebra_records_retrieve (ZebraHandle zh, ODR stream,
761                              const char *setname, Z_RecordComposition *comp,
762                              oid_value input_format, int num_recs,
763                              ZebraRetrievalRecord *recs)
764 {
765     ZebraPosSet poset;
766     int i, *pos_array;
767     ASSERTZH;
768     zh->errCode=0;
769
770     if (!zh->res)
771     {
772         zh->errCode = 30;
773         zh->errString = odr_strdup (stream, setname);
774         return;
775     }
776     
777     zh->errCode = 0;
778
779     if (zebra_begin_read (zh))
780         return;
781
782     pos_array = (int *) xmalloc (num_recs * sizeof(*pos_array));
783     for (i = 0; i<num_recs; i++)
784         pos_array[i] = recs[i].position;
785     poset = zebraPosSetCreate (zh, setname, num_recs, pos_array);
786     if (!poset)
787     {
788         logf (LOG_DEBUG, "zebraPosSetCreate error");
789         zh->errCode = 30;
790         zh->errString = nmem_strdup (stream->mem, setname);
791     }
792     else
793     {
794         for (i = 0; i<num_recs; i++)
795         {
796             if (poset[i].term)
797             {
798                 recs[i].errCode = 0;
799                 recs[i].format = VAL_SUTRS;
800                 recs[i].len = strlen(poset[i].term);
801                 recs[i].buf = poset[i].term;
802                 recs[i].base = poset[i].db;
803             }
804             else if (poset[i].sysno)
805             {
806                 recs[i].errCode =
807                     zebra_record_fetch (zh, poset[i].sysno, poset[i].score,
808                                         stream, input_format, comp,
809                                         &recs[i].format, &recs[i].buf,
810                                         &recs[i].len,
811                                         &recs[i].base);
812                 recs[i].errString = NULL;
813             }
814             else
815             {
816                 char num_str[20];
817
818                 sprintf (num_str, "%d", pos_array[i]);  
819                 zh->errCode = 13;
820                 zh->errString = odr_strdup (stream, num_str);
821                 break;
822             }
823         }
824         zebraPosSetDestroy (zh, poset, num_recs);
825     }
826     zebra_end_read (zh);
827     xfree (pos_array);
828 }
829
830 void zebra_scan (ZebraHandle zh, ODR stream, Z_AttributesPlusTerm *zapt,
831                  oid_value attributeset,
832                  int *position, int *num_entries, ZebraScanEntry **entries,
833                  int *is_partial)
834 {
835     ASSERTZH;
836     zh->errCode=0;
837     if (zebra_begin_read (zh))
838     {
839         *entries = 0;
840         *num_entries = 0;
841         return;
842     }
843     rpn_scan (zh, stream, zapt, attributeset,
844               zh->num_basenames, zh->basenames, position,
845               num_entries, entries, is_partial);
846     zebra_end_read (zh);
847 }
848
849 void zebra_sort (ZebraHandle zh, ODR stream,
850                  int num_input_setnames, const char **input_setnames,
851                  const char *output_setname, Z_SortKeySpecList *sort_sequence,
852                  int *sort_status)
853 {
854     ASSERTZH;
855     zh->errCode=0;
856     if (zebra_begin_read (zh))
857         return;
858     resultSetSort (zh, stream->mem, num_input_setnames, input_setnames,
859                    output_setname, sort_sequence, sort_status);
860     zebra_end_read(zh);
861 }
862
863 int zebra_deleleResultSet(ZebraHandle zh, int function,
864                           int num_setnames, char **setnames,
865                           int *statuses)
866 {
867     int i, status;
868     ASSERTZH;
869     zh->errCode=0;
870     if (zebra_begin_read(zh))
871         return Z_DeleteStatus_systemProblemAtTarget;
872     switch (function)
873     {
874     case Z_DeleteRequest_list:
875         resultSetDestroy (zh, num_setnames, setnames, statuses);
876         break;
877     case Z_DeleteRequest_all:
878         resultSetDestroy (zh, -1, 0, statuses);
879         break;
880     }
881     zebra_end_read (zh);
882     status = Z_DeleteStatus_success;
883     for (i = 0; i<num_setnames; i++)
884         if (statuses[i] == Z_DeleteStatus_resultSetDidNotExist)
885             status = statuses[i];
886     return status;
887 }
888
889 int zebra_errCode (ZebraHandle zh)
890 {
891     if (zh)
892         return zh->errCode;
893     return 0; 
894 }
895
896 const char *zebra_errString (ZebraHandle zh)
897 {
898     if (zh)
899         return diagbib1_str (zh->errCode);
900     return "";
901 }
902
903 char *zebra_errAdd (ZebraHandle zh)
904 {
905     if (zh)
906         return zh->errString;
907     return "";
908 }
909
910 int zebra_auth (ZebraHandle zh, const char *user, const char *pass)
911 {
912     ZebraService zs;
913     ASSERTZH;
914     zh->errCode=0;
915     zs= zh->service;
916     if (!zs->passwd_db || !passwd_db_auth (zs->passwd_db, user, pass))
917     {
918         logf(LOG_APP,"AUTHOK:%s", user?user:"ANONYMOUS");
919         return 0;
920     }
921
922     logf(LOG_APP,"AUTHFAIL:%s", user?user:"ANONYMOUS");
923     return 1;
924 }
925
926 void zebra_admin_import_begin (ZebraHandle zh, const char *database,
927                                const char *record_type)
928 {
929     ASSERTZH;
930     zh->errCode=0;
931     if (zebra_select_database(zh, database))
932         return;
933     zebra_begin_trans (zh);
934     xfree (zh->admin_databaseName);
935     zh->admin_databaseName = xstrdup(database);
936 }
937
938 void zebra_admin_import_end (ZebraHandle zh)
939 {
940     ASSERTZH;
941     zh->errCode=0;
942     zebra_end_trans (zh);
943 }
944
945 void zebra_admin_import_segment (ZebraHandle zh, Z_Segment *segment)
946 {
947     int sysno;
948     int i;
949     ASSERTZH;
950     zh->errCode=0;
951     for (i = 0; i<segment->num_segmentRecords; i++)
952     {
953         Z_NamePlusRecord *npr = segment->segmentRecords[i];
954         const char *databaseName = npr->databaseName;
955
956         if (!databaseName)
957             databaseName = zh->admin_databaseName;
958         printf ("--------------%d--------------------\n", i);
959         if (npr->which == Z_NamePlusRecord_intermediateFragment)
960         {
961             Z_FragmentSyntax *fragment = npr->u.intermediateFragment;
962             if (fragment->which == Z_FragmentSyntax_notExternallyTagged)
963             {
964                 Odr_oct *oct = fragment->u.notExternallyTagged;
965                 printf ("%.*s", (oct->len > 100 ? 100 : oct->len) ,
966                         oct->buf);
967                 
968                 sysno = 0;
969                 extract_rec_in_mem (zh, "grs.sgml",
970                                     oct->buf, oct->len,
971                                     databaseName,
972                                     0 /* delete_flag */,
973                                     0 /* test_mode */,
974                                     &sysno /* sysno */,
975                                     1 /* store_keys */,
976                                     1 /* store_data */,
977                                     0 /* match criteria */);
978             }
979         }
980     }
981 }
982
983 int zebra_admin_exchange_record (ZebraHandle zh,
984                                  const char *database,
985                                  const char *rec_buf,
986                                  size_t rec_len,
987                                  const char *recid_buf, size_t recid_len,
988                                  int action)
989 {
990     int sysno = 0;
991     char *rinfo = 0;
992     char recid_z[256];
993     ASSERTZH;
994     zh->errCode=0;
995
996     if (!recid_buf || recid_len <= 0 || recid_len >= sizeof(recid_z))
997         return -1;
998     memcpy (recid_z, recid_buf, recid_len);
999     recid_z[recid_len] = 0;
1000
1001     rinfo = dict_lookup (zh->reg->matchDict, recid_z);
1002     if (rinfo)
1003     {
1004         if (action == 1)  /* fail if insert */
1005             return -1;
1006         memcpy (&sysno, rinfo+1, sizeof(sysno));
1007     }
1008     else
1009     {
1010         if (action == 2 || action == 3) /* fail if delete or update */
1011             return -1;
1012     }
1013     extract_rec_in_mem (zh, "grs.sgml", rec_buf, rec_len, database,
1014                         action == 3 ? 1 : 0 /* delete flag */,
1015                         0, &sysno, 1, 1, 0);
1016     if (action == 1)
1017     {
1018         dict_insert (zh->reg->matchDict, recid_z, sizeof(sysno), &sysno);
1019     }
1020     else if (action == 3)
1021     {
1022         dict_delete (zh->reg->matchDict, recid_z);
1023     }
1024     return 0;
1025 }
1026
1027 void zebra_admin_create (ZebraHandle zh, const char *database)
1028 {
1029     ZebraService zs;
1030     ASSERTZH;
1031     zh->errCode=0;
1032
1033     if (zebra_select_database (zh, database))
1034         return;
1035     zebra_begin_trans (zh);
1036
1037     zs = zh->service;
1038     /* announce database */
1039     if (zebraExplain_newDatabase (zh->reg->zei, database, 0 
1040                                   /* explainDatabase */))
1041     {
1042         zh->errCode = 224;
1043         zh->errString = "database already exist";
1044     }
1045     zebra_end_trans (zh);
1046 }
1047
1048 int zebra_string_norm (ZebraHandle zh, unsigned reg_id,
1049                        const char *input_str, int input_len,
1050                        char *output_str, int output_len)
1051 {
1052     WRBUF wrbuf;
1053     ASSERTZH;
1054     zh->errCode=0;
1055     if (!zh->reg->zebra_maps)
1056         return -1;
1057     wrbuf = zebra_replace(zh->reg->zebra_maps, reg_id, "",
1058                           input_str, input_len);
1059     if (!wrbuf)
1060         return -2;
1061     if (wrbuf_len(wrbuf) >= output_len)
1062         return -3;
1063     if (wrbuf_len(wrbuf))
1064         memcpy (output_str, wrbuf_buf(wrbuf), wrbuf_len(wrbuf));
1065     output_str[wrbuf_len(wrbuf)] = '\0';
1066     return wrbuf_len(wrbuf);
1067 }
1068
1069
1070 void zebra_set_state (ZebraHandle zh, int val, int seqno)
1071 {
1072     char state_fname[256];
1073     char *fname;
1074     long p = getpid();
1075     FILE *f;
1076     ASSERTZH;
1077     zh->errCode=0;
1078
1079     sprintf (state_fname, "state.%s.LCK", zh->reg_name);
1080     fname = zebra_mk_fname (res_get(zh->res, "lockDir"), state_fname);
1081     f = fopen (fname, "w");
1082
1083     yaz_log (LOG_DEBUG, "%c %d %ld", val, seqno, p);
1084     fprintf (f, "%c %d %ld\n", val, seqno, p);
1085     fclose (f);
1086     xfree (fname);
1087 }
1088
1089 void zebra_get_state (ZebraHandle zh, char *val, int *seqno)
1090 {
1091     char state_fname[256];
1092     char *fname;
1093     FILE *f;
1094
1095     ASSERTZH;
1096     zh->errCode=0;
1097     sprintf (state_fname, "state.%s.LCK", zh->reg_name);
1098     fname = zebra_mk_fname (res_get(zh->res, "lockDir"), state_fname);
1099     f = fopen (fname, "r");
1100     *val = 'o';
1101     *seqno = 0;
1102
1103     if (f)
1104     {
1105         fscanf (f, "%c %d", val, seqno);
1106         fclose (f);
1107     }
1108     xfree (fname);
1109 }
1110
1111 int zebra_begin_read (ZebraHandle zh)
1112 {
1113     int dirty = 0;
1114     char val;
1115     int seqno;
1116     ASSERTZH;
1117
1118     assert (zh->res);
1119
1120     (zh->trans_no)++;
1121
1122     if (zh->trans_no != 1)
1123     {
1124         zebra_flush_reg (zh);
1125         return 0;
1126     }
1127     zh->errCode=0;
1128 #if HAVE_SYS_TIMES_H
1129     times (&zh->tms1);
1130 #endif
1131     if (!zh->res)
1132     {
1133         (zh->trans_no)--;
1134         zh->errCode = 109;
1135         return -1;
1136     }
1137     if (!zh->lock_normal || !zh->lock_shadow)
1138     {
1139         (zh->trans_no)--;
1140         zh->errCode = 2;
1141         return -1;
1142     }
1143     zebra_get_state (zh, &val, &seqno);
1144     if (val == 'd')
1145         val = 'o';
1146
1147     if (!zh->reg)
1148         dirty = 1;
1149     else if (seqno != zh->reg->seqno)
1150     {
1151         yaz_log (LOG_LOG, "reopen seqno cur/old %d/%d",
1152                  seqno, zh->reg->seqno);
1153         dirty = 1;
1154     }
1155     else if (zh->reg->last_val != val)
1156     {
1157         yaz_log (LOG_LOG, "reopen last cur/old %d/%d",
1158                  val, zh->reg->last_val);
1159         dirty = 1;
1160     }
1161     if (!dirty)
1162         return 0;
1163
1164     if (val == 'c')
1165         zebra_lock_r (zh->lock_shadow);
1166     else
1167         zebra_lock_r (zh->lock_normal);
1168     
1169     if (zh->reg)
1170         zebra_register_close (zh->service, zh->reg);
1171     zh->reg = zebra_register_open (zh->service, zh->reg_name,
1172                                    0, val == 'c' ? 1 : 0,
1173                                    zh->res, zh->path_reg);
1174     if (!zh->reg)
1175     {
1176         zh->errCode = 109;
1177         return -1;
1178     }
1179     zh->reg->last_val = val;
1180     zh->reg->seqno = seqno;
1181
1182     return 0;
1183 }
1184
1185 void zebra_end_read (ZebraHandle zh)
1186 {
1187     ASSERTZH;
1188     (zh->trans_no)--;
1189
1190     if (zh->trans_no != 0)
1191         return;
1192 #if HAVE_SYS_TIMES_H
1193     times (&zh->tms2);
1194     logf (LOG_LOG, "user/system: %ld/%ld",
1195                     (long) (zh->tms2.tms_utime - zh->tms1.tms_utime),
1196                     (long) (zh->tms2.tms_stime - zh->tms1.tms_stime));
1197
1198 #endif
1199
1200     zebra_unlock (zh->lock_normal);
1201     zebra_unlock (zh->lock_shadow);
1202 }
1203
1204 void zebra_begin_trans (ZebraHandle zh)
1205 {
1206     int pass;
1207     int seqno = 0;
1208     char val = '?';
1209     const char *rval = 0;
1210     ASSERTZHRES;
1211
1212     assert (zh->res);
1213
1214     (zh->trans_no++);
1215     if (zh->trans_no != 1)
1216     {
1217         return;
1218     }
1219     zh->errCode=0;
1220     
1221     yaz_log (LOG_LOG, "zebra_begin_trans");
1222
1223     zh->records_inserted = 0;
1224     zh->records_updated = 0;
1225     zh->records_deleted = 0;
1226     zh->records_processed = 0;
1227
1228 #if HAVE_SYS_TIMES_H
1229     times (&zh->tms1);
1230 #endif
1231     
1232     /* lock */
1233     if (zh->shadow_enable)
1234         rval = res_get (zh->res, "shadow");
1235
1236     for (pass = 0; pass < 2; pass++)
1237     {
1238         if (rval)
1239         {
1240             zebra_lock_r (zh->lock_normal);
1241             zebra_lock_w (zh->lock_shadow);
1242         }
1243         else
1244         {
1245             zebra_lock_w (zh->lock_normal);
1246             zebra_lock_w (zh->lock_shadow);
1247         }
1248         
1249         zebra_get_state (zh, &val, &seqno);
1250         if (val == 'c')
1251         {
1252             yaz_log (LOG_LOG, "previous transaction didn't finish commit");
1253             zebra_unlock (zh->lock_shadow);
1254             zebra_unlock (zh->lock_normal);
1255             zebra_commit (zh);
1256             continue;
1257         }
1258         else if (val == 'd')
1259         {
1260             if (rval)
1261             {
1262                 BFiles bfs = bfs_create (res_get (zh->res, "shadow"),
1263                                          zh->path_reg);
1264                 yaz_log (LOG_LOG, "previous transaction didn't reach commit");
1265                 bf_commitClean (bfs, rval);
1266                 bfs_destroy (bfs);
1267             }
1268             else
1269             {
1270                 yaz_log (LOG_WARN, "your previous transaction didn't finish");
1271             }
1272         }
1273         break;
1274     }
1275     if (pass == 2)
1276     {
1277         yaz_log (LOG_FATAL, "zebra_begin_trans couldn't finish commit");
1278         abort();
1279         return;
1280     }
1281     zebra_set_state (zh, 'd', seqno);
1282
1283     zh->reg = zebra_register_open (zh->service, zh->reg_name,
1284                                    1, rval ? 1 : 0, zh->res,
1285                                    zh->path_reg);
1286
1287     zh->reg->seqno = seqno;
1288 }
1289
1290 void zebra_end_trans (ZebraHandle zh) {
1291   ZebraTransactionStatus dummy;
1292   zebra_end_transaction(zh, &dummy);
1293 }
1294
1295 void zebra_end_transaction (ZebraHandle zh, ZebraTransactionStatus *status)
1296 {
1297     char val;
1298     int seqno;
1299     const char *rval;
1300
1301     ASSERTZH;
1302
1303     status->processed = 0;
1304     status->inserted  = 0;
1305     status->updated   = 0;
1306     status->deleted   = 0;
1307     status->utime     = 0;
1308     status->stime     = 0;
1309
1310     zh->trans_no--;
1311     if (zh->trans_no != 0)
1312         return;
1313     yaz_log (LOG_LOG, "zebra_end_trans");
1314     rval = res_get (zh->res, "shadow");
1315
1316     zebraExplain_runNumberIncrement (zh->reg->zei, 1);
1317
1318     zebra_flush_reg (zh);
1319
1320     zebra_register_close (zh->service, zh->reg);
1321     zh->reg = 0;
1322
1323     yaz_log (LOG_LOG, "Records: %7d i/u/d %d/%d/%d", 
1324              zh->records_processed, zh->records_inserted,
1325              zh->records_updated, zh->records_deleted);
1326
1327     status->processed = zh->records_processed;
1328     status->inserted = zh->records_inserted;
1329     status->updated = zh->records_updated;
1330     status->deleted = zh->records_deleted;
1331
1332     zebra_get_state (zh, &val, &seqno);
1333     if (val != 'd')
1334     {
1335         BFiles bfs = bfs_create (rval, zh->path_reg);
1336         yaz_log (LOG_LOG, "deleting shadow stuff val=%c", val);
1337         bf_commitClean (bfs, rval);
1338         bfs_destroy (bfs);
1339     }
1340     if (!rval)
1341         seqno++;
1342     zebra_set_state (zh, 'o', seqno);
1343
1344     zebra_unlock (zh->lock_shadow);
1345     zebra_unlock (zh->lock_normal);
1346
1347 #if HAVE_SYS_TIMES_H
1348     times (&zh->tms2);
1349     logf (LOG_LOG, "user/system: %ld/%ld",
1350                     (long) (zh->tms2.tms_utime - zh->tms1.tms_utime),
1351                     (long) (zh->tms2.tms_stime - zh->tms1.tms_stime));
1352
1353     status->utime = (long) (zh->tms2.tms_utime - zh->tms1.tms_utime);
1354     status->stime = (long) (zh->tms2.tms_stime - zh->tms1.tms_stime);
1355 #endif
1356
1357     return;
1358 }
1359
1360 void zebra_repository_update (ZebraHandle zh)
1361 {
1362     ASSERTZH;
1363     zh->errCode=0;
1364     logf (LOG_LOG, "updating %s", zh->rGroup.path);
1365     repositoryUpdate (zh);    
1366 }
1367
1368 void zebra_repository_delete (ZebraHandle zh)
1369 {
1370     ASSERTZH;
1371     zh->errCode=0;
1372     logf (LOG_LOG, "deleting %s", zh->rGroup.path);
1373     repositoryDelete (zh);
1374 }
1375
1376 void zebra_repository_show (ZebraHandle zh)
1377 {
1378     ASSERTZH;
1379     zh->errCode=0;
1380     repositoryShow (zh);
1381 }
1382
1383 int zebra_commit (ZebraHandle zh)
1384 {
1385     int seqno;
1386     char val;
1387     const char *rval;
1388     BFiles bfs;
1389     ASSERTZH;
1390     zh->errCode=0;
1391
1392     if (!zh->res)
1393     {
1394         zh->errCode = 109;
1395         return -1;
1396     }
1397     rval = res_get (zh->res, "shadow");    
1398     if (!rval)
1399     {
1400         logf (LOG_WARN, "Cannot perform commit");
1401         logf (LOG_WARN, "No shadow area defined");
1402         return 0;
1403     }
1404
1405     zebra_lock_w (zh->lock_normal);
1406     zebra_lock_r (zh->lock_shadow);
1407
1408     bfs = bfs_create (res_get (zh->res, "register"), zh->path_reg);
1409
1410     zebra_get_state (zh, &val, &seqno);
1411
1412     if (rval && *rval)
1413         bf_cache (bfs, rval);
1414     if (bf_commitExists (bfs))
1415     {
1416         zebra_set_state (zh, 'c', seqno);
1417
1418         logf (LOG_LOG, "commit start");
1419         bf_commitExec (bfs);
1420 #ifndef WIN32
1421         sync ();
1422 #endif
1423         logf (LOG_LOG, "commit clean");
1424         bf_commitClean (bfs, rval);
1425         seqno++;
1426         zebra_set_state (zh, 'o', seqno);
1427     }
1428     else
1429     {
1430         logf (LOG_LOG, "nothing to commit");
1431     }
1432     bfs_destroy (bfs);
1433
1434     zebra_unlock (zh->lock_shadow);
1435     zebra_unlock (zh->lock_normal);
1436     return 0;
1437 }
1438
1439 int zebra_init (ZebraHandle zh)
1440 {
1441     const char *rval;
1442     BFiles bfs = 0;
1443     ASSERTZH;
1444     zh->errCode=0;
1445
1446     if (!zh->res)
1447     {
1448         zh->errCode = 109;
1449         return -1;
1450     }
1451     rval = res_get (zh->res, "shadow");
1452
1453     bfs = bfs_create (res_get (zh->service->global_res, "register"),
1454                       zh->path_reg);
1455     if (rval && *rval)
1456         bf_cache (bfs, rval);
1457     
1458     bf_reset (bfs);
1459     bfs_destroy (bfs);
1460     zebra_set_state (zh, 'o', 0);
1461     return 0;
1462 }
1463
1464 int zebra_compact (ZebraHandle zh)
1465 {
1466     BFiles bfs;
1467     ASSERTZH;
1468     zh->errCode=0;
1469     if (!zh->res)
1470     {
1471         zh->errCode = 109;
1472         return -1;
1473     }
1474     bfs = bfs_create (res_get (zh->res, "register"), zh->path_reg);
1475     inv_compact (bfs);
1476     bfs_destroy (bfs);
1477     return 0;
1478 }
1479
1480 int zebra_record_insert (ZebraHandle zh, const char *buf, int len)
1481 {
1482     int sysno = 0;
1483     int olderr;
1484     ASSERTZH;
1485     zh->errCode=0;
1486     zebra_begin_trans (zh);
1487     if (zh->errCode)
1488       return 0; /* bad sysno */
1489     extract_rec_in_mem (zh, "grs.sgml",
1490                         buf, len,
1491                         "Default",  /* database */
1492                         0 /* delete_flag */,
1493                         0 /* test_mode */,
1494                         &sysno /* sysno */,
1495                         1 /* store_keys */,
1496                         1 /* store_data */,
1497                         0 /* match criteria */);
1498     olderr=zh->errCode;
1499     zebra_end_trans (zh);
1500     if (olderr)
1501       zh->errCode=olderr; 
1502     return sysno;
1503 }
1504
1505 void zebra_set_group (ZebraHandle zh, struct recordGroup *rg)
1506 {
1507     ASSERTZH;
1508     zh->errCode=0;
1509     memcpy (&zh->rGroup, rg, sizeof(*rg));
1510 }
1511
1512 void zebra_result (ZebraHandle zh, int *code, char **addinfo)
1513 {
1514     ASSERTZH;
1515     *code = zh->errCode;
1516     *addinfo = zh->errString;
1517 }
1518
1519 void zebra_shadow_enable (ZebraHandle zh, int value)
1520 {
1521     ASSERTZH;
1522     zh->errCode=0;
1523     zh->shadow_enable = value;
1524 }
1525
1526 int zebra_record_encoding (ZebraHandle zh, const char *encoding)
1527 {
1528     ASSERTZH;
1529     zh->errCode=0;
1530     xfree (zh->record_encoding);
1531
1532     /*
1533      * Fixme!
1534      */
1535
1536     if (zh->iconv_to_utf8 != 0)
1537         yaz_iconv_close(zh->iconv_to_utf8);
1538     if (zh->iconv_from_utf8 != 0)
1539         yaz_iconv_close(zh->iconv_from_utf8);
1540     
1541     zh->record_encoding = xstrdup (encoding);
1542     
1543     logf(LOG_DEBUG, "Reset record encoding: %s", encoding);
1544     
1545     zh->iconv_to_utf8 =
1546         yaz_iconv_open ("UTF-8", encoding);
1547     if (zh->iconv_to_utf8 == 0)
1548         yaz_log (LOG_WARN, "iconv: %s to UTF-8 unsupported", encoding);
1549     zh->iconv_from_utf8 =
1550         yaz_iconv_open (encoding, "UTF-8");
1551     if (zh->iconv_to_utf8 == 0)
1552         yaz_log (LOG_WARN, "iconv: UTF-8 to %s unsupported", encoding);
1553
1554     return 0;
1555 }
1556
1557 void zebra_set_resource(ZebraHandle zh, const char *name, const char *value)
1558 {
1559     ASSERTZH;
1560     zh->errCode=0;
1561     res_put(zh->res, name, value);
1562 }
1563
1564 const char *zebra_get_resource(ZebraHandle zh,
1565                                 const char *name, const char *defaultvalue)
1566 {
1567     ASSERTZH;
1568     zh->errCode=0;
1569     return res_get_def( zh->res, name, (char *)defaultvalue);
1570 }