ea7189268ef6c60ca9026bcfeef747dec1f89f61
[phpyaz-moved-to-github.git] / php_yaz.c
1 /*
2    +----------------------------------------------------------------------+
3    | PHP Version 5                                                        |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 1997-2004 The PHP Group                                |
6    +----------------------------------------------------------------------+
7    | This source file is subject to version 3.0 of the PHP license,       |
8    | that is bundled with this package in the file LICENSE, and is        |
9    | available through the world-wide-web at the following url:           |
10    | http://www.php.net/license/3_0.txt.                                  |
11    | If you did not receive a copy of the PHP license and are unable to   |
12    | obtain it through the world-wide-web, please send a note to          |
13    | license@php.net so we can mail you a copy immediately.               |
14    +----------------------------------------------------------------------+
15    | Author: Adam Dickmeiss <adam@indexdata.dk>                           |
16    +----------------------------------------------------------------------+
17  */
18
19 /* $Id: php_yaz.c,v 1.115 2008/02/20 10:08:15 dickmeiss Exp $ */
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #include "php.h"
26 #include "php_ini.h"
27
28 #if HAVE_YAZ
29
30 #include "ext/standard/info.h"
31 #include "php_yaz.h"
32
33 #include <yaz/yaz-version.h>
34
35 #ifndef YAZ_VERSIONL
36 #error YAZ version 3.0 or later must be used.
37 #elif YAZ_VERSIONL < 0x030000
38 #error YAZ version 3.0 or later must be used.
39 #endif
40
41 #ifdef PHP_WIN32
42 #include <process.h>
43 #endif
44
45 #include <yaz/log.h>
46 #include <yaz/proto.h>
47 #include <yaz/marcdisp.h>
48 #include <yaz/yaz-util.h>
49 #include <yaz/yaz-ccl.h>
50 #include <yaz/oid_db.h>
51 #include <yaz/zoom.h>
52
53 #ifndef ODR_INT_PRINTF
54 #define ODR_INT_PRINTF %d
55 #endif
56
57 #define MAX_ASSOC 200
58
59 typedef struct Yaz_AssociationInfo *Yaz_Association;
60
61 struct Yaz_AssociationInfo {
62         CCL_bibset bibset;
63         ZOOM_connection zoom_conn;
64         ZOOM_resultset zoom_set;
65         ZOOM_scanset zoom_scan;
66         ZOOM_package zoom_package;
67         char *sort_criteria;
68         int persistent;
69         int in_use;
70         int order;
71         int zval_resource;
72         long time_stamp;
73 };
74
75 static Yaz_Association yaz_association_mk()
76 {
77         Yaz_Association p = xmalloc (sizeof(*p));
78
79         p->zoom_conn = ZOOM_connection_create (0);
80         p->zoom_set = 0;
81         p->zoom_scan = 0;
82         p->zoom_package = 0;
83         ZOOM_connection_option_set(p->zoom_conn, "implementationName", "PHP");
84         ZOOM_connection_option_set(p->zoom_conn, "async", "1");
85         p->sort_criteria = 0;
86         p->in_use = 0;
87         p->order = 0;
88         p->persistent = 0;
89         p->bibset = ccl_qual_mk();
90         p->time_stamp = 0;
91         return p;
92 }
93
94 static void yaz_association_destroy (Yaz_Association p)
95 {
96         if (!p) {
97                 return;
98         }
99
100         ZOOM_resultset_destroy(p->zoom_set);
101         ZOOM_scanset_destroy(p->zoom_scan);
102         ZOOM_package_destroy(p->zoom_package);
103         ZOOM_connection_destroy(p->zoom_conn);
104         xfree(p->sort_criteria);
105         ccl_qual_rm(&p->bibset);
106 }
107
108 #ifdef ZTS
109 static MUTEX_T yaz_mutex;
110 #endif
111
112 ZEND_DECLARE_MODULE_GLOBALS(yaz);
113
114 static Yaz_Association *shared_associations;
115 static int order_associations;
116 static int le_link;
117
118
119 #ifdef COMPILE_DL_YAZ
120 ZEND_GET_MODULE(yaz)
121 #endif
122
123 #ifdef ZEND_BEGIN_ARG_INFO
124     ZEND_BEGIN_ARG_INFO(first_argument_force_ref, 0)
125         ZEND_ARG_PASS_INFO(1)
126     ZEND_END_ARG_INFO();
127
128     ZEND_BEGIN_ARG_INFO(second_argument_force_ref, 0)
129         ZEND_ARG_PASS_INFO(0)
130         ZEND_ARG_PASS_INFO(1)
131     ZEND_END_ARG_INFO();
132
133     ZEND_BEGIN_ARG_INFO(third_argument_force_ref, 0)
134         ZEND_ARG_PASS_INFO(0)
135         ZEND_ARG_PASS_INFO(0)
136         ZEND_ARG_PASS_INFO(1)
137     ZEND_END_ARG_INFO();
138 #else
139 static unsigned char first_argument_force_ref[] = {
140         1, BYREF_FORCE };
141 static unsigned char second_argument_force_ref[] = {
142         2, BYREF_NONE, BYREF_FORCE };
143 static unsigned char third_argument_force_ref[] = {
144         3, BYREF_NONE, BYREF_NONE, BYREF_FORCE };
145 #endif
146
147
148 function_entry yaz_functions [] = {
149         PHP_FE(yaz_connect, NULL)
150         PHP_FE(yaz_close, NULL)
151         PHP_FE(yaz_search, NULL)
152         PHP_FE(yaz_wait, first_argument_force_ref)
153         PHP_FE(yaz_errno, NULL)
154         PHP_FE(yaz_error, NULL)
155         PHP_FE(yaz_addinfo, NULL)
156         PHP_FE(yaz_hits, second_argument_force_ref)
157         PHP_FE(yaz_record, NULL)
158         PHP_FE(yaz_syntax, NULL)
159         PHP_FE(yaz_element, NULL)
160         PHP_FE(yaz_range, NULL)
161         PHP_FE(yaz_itemorder, NULL)
162         PHP_FE(yaz_es_result, NULL)
163         PHP_FE(yaz_scan, NULL)
164         PHP_FE(yaz_scan_result, second_argument_force_ref)
165         PHP_FE(yaz_present, NULL)
166         PHP_FE(yaz_ccl_conf, NULL)
167         PHP_FE(yaz_ccl_parse, third_argument_force_ref)
168         PHP_FE(yaz_database, NULL)
169         PHP_FE(yaz_sort, NULL)
170         PHP_FE(yaz_schema, NULL)
171         PHP_FE(yaz_set_option, NULL)
172         PHP_FE(yaz_get_option, NULL)
173         PHP_FE(yaz_es, NULL)
174         {NULL, NULL, NULL}
175 };
176
177 static void get_assoc(INTERNAL_FUNCTION_PARAMETERS, pval *id, Yaz_Association *assocp)
178 {
179         Yaz_Association *as = 0;
180         
181         *assocp = 0;
182 #ifdef ZTS
183         tsrm_mutex_lock (yaz_mutex);
184 #endif
185
186         ZEND_FETCH_RESOURCE(as, Yaz_Association *, &id, -1, "YAZ link", le_link);
187
188         if (as && *as && (*as)->order == YAZSG(assoc_seq) && (*as)->in_use) {
189                 *assocp = *as;
190         } else {
191 #ifdef ZTS
192                 tsrm_mutex_unlock (yaz_mutex);
193 #endif
194                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Invalid YAZ handle");
195         }
196 }
197
198 static void release_assoc(Yaz_Association assoc)
199 {
200 #ifdef ZTS
201         if (assoc) {
202                 tsrm_mutex_unlock(yaz_mutex);
203         }
204 #endif
205 }
206
207 static const char *array_lookup_string(HashTable *ht, const char *idx)
208 {
209         pval **pvalue;
210
211         if (ht && zend_hash_find(ht, (char *) idx, strlen(idx) + 1, (void **) &pvalue) == SUCCESS) {
212                 SEPARATE_ZVAL(pvalue);
213                 convert_to_string(*pvalue);
214                 return (*pvalue)->value.str.val;
215         }
216         return 0;
217 }
218
219 static long *array_lookup_long(HashTable *ht, const char *idx)
220 {
221         pval **pvalue;
222
223         if (ht && zend_hash_find(ht, (char *) idx, strlen(idx) + 1, (void **) &pvalue) == SUCCESS) {
224                 SEPARATE_ZVAL(pvalue);
225                 convert_to_long(*pvalue);
226                 return &(*pvalue)->value.lval;
227         }
228         return 0;
229 }
230
231 static long *array_lookup_bool(HashTable *ht, const char *idx)
232 {
233         pval **pvalue;
234
235         if (ht && zend_hash_find(ht, (char *) idx, strlen(idx) + 1, (void **) &pvalue) == SUCCESS) {
236                 SEPARATE_ZVAL(pvalue);
237                 convert_to_boolean(*pvalue);
238                 return &(*pvalue)->value.lval;
239         }
240         return 0;
241 }
242
243 static const char *option_get(Yaz_Association as, const char *name)
244 {
245         if (!as) {
246                 return 0;
247         }
248         return ZOOM_connection_option_get(as->zoom_conn, name);
249 }
250
251 static int option_get_int(Yaz_Association as, const char *name, int def)
252 {
253         const char *v;
254
255         v = ZOOM_connection_option_get(as->zoom_conn, name);
256
257         if (!v) {
258                 return def;
259         }
260
261         return atoi(v);
262 }
263
264 static void option_set(Yaz_Association as, const char *name, const char *value)
265 {
266         if (as && value) {
267                 ZOOM_connection_option_set(as->zoom_conn, name, value);
268         }
269 }
270
271 static void option_set_int(Yaz_Association as, const char *name, int v)
272 {
273         if (as) {
274                 char s[30];
275
276                 sprintf (s, "%d", v);
277                 ZOOM_connection_option_set(as->zoom_conn, name, s);
278         }
279 }
280
281 static int strcmp_null(const char *s1, const char *s2)
282 {
283         if (s1 == 0 && s2 == 0) {
284                 return 0;
285         }
286         if (s1 == 0 || s2 == 0) {
287                 return -1;
288         }
289         return strcmp(s1, s2);
290 }
291
292 /* {{{ proto resource yaz_connect(string zurl [, array options])
293    Create target with given zurl. Returns positive id if successful. */
294 PHP_FUNCTION(yaz_connect)
295 {
296         int i;
297         char *cp;
298         char *zurl_str;
299         const char *sru_str = 0, *sru_version_str = 0;
300         const char *user_str = 0, *group_str = 0, *pass_str = 0;
301         const char *cookie_str = 0, *proxy_str = 0;
302         const char *charset_str = 0;
303         const char *client_IP = 0;
304         const char *otherInfo[3];
305         const char *maximumRecordSize = 0;
306         const char *preferredMessageSize = 0;
307         int persistent = 1;
308         int piggyback = 1;
309         pval *zurl, *user = 0;
310         Yaz_Association as;
311         int max_links = YAZSG(max_links);
312
313         otherInfo[0] = otherInfo[1] = otherInfo[2] = 0;
314
315         if (ZEND_NUM_ARGS() == 1) {
316                 if (zend_parse_parameters(1, "z", &zurl)
317                         == FAILURE) {
318                         WRONG_PARAM_COUNT;
319                 }
320         } else if (ZEND_NUM_ARGS() == 2) {
321                 if (zend_parse_parameters(2, "zz", &zurl, &user)
322                         == FAILURE) {
323                         WRONG_PARAM_COUNT;
324                 }
325                 
326                 if (Z_TYPE_PP(&user) == IS_ARRAY) {
327                         long *persistent_val;
328                         long *piggyback_val;
329                         HashTable *ht = Z_ARRVAL_PP(&user);
330                         
331                         sru_str = array_lookup_string(ht, "sru");
332                         sru_version_str = array_lookup_string(ht, "sru_version");
333                         user_str = array_lookup_string(ht, "user");
334                         group_str = array_lookup_string(ht, "group");
335                         pass_str = array_lookup_string(ht, "password");
336                         cookie_str = array_lookup_string(ht, "cookie");
337                         proxy_str = array_lookup_string(ht, "proxy");
338                         charset_str = array_lookup_string(ht, "charset");
339                         persistent_val = array_lookup_bool(ht, "persistent");
340                         if (persistent_val) {
341                                 persistent = *persistent_val;
342                         }
343                         piggyback_val = array_lookup_bool(ht, "piggyback");
344                         if (piggyback_val) {
345                                 piggyback = *piggyback_val;
346                         }
347                         maximumRecordSize =
348                                 array_lookup_string(ht, "maximumRecordSize");
349                         preferredMessageSize =
350                                 array_lookup_string(ht, "preferredMessageSize");
351                         otherInfo[0] = array_lookup_string(ht, "otherInfo0");
352                         otherInfo[1] = array_lookup_string(ht, "otherInfo1");
353                         otherInfo[2] = array_lookup_string(ht, "otherInfo2");
354                 } else if (Z_TYPE_PP(&user) == IS_STRING) {
355                         convert_to_string_ex(&user);
356                         if (*user->value.str.val)
357                                 user_str = user->value.str.val;
358                 }
359         } else {
360                 WRONG_PARAM_COUNT;
361         }
362         convert_to_string_ex(&zurl);
363         zurl_str = zurl->value.str.val;
364         for (cp = zurl_str; *cp && strchr("\t\n ", *cp); cp++);
365         if (!*cp) {
366                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Empty zurl");
367                 RETURN_LONG(0);
368         }
369         /* see if we have it already ... */
370 #ifdef ZTS
371         tsrm_mutex_lock(yaz_mutex);
372 #endif
373         for (i = 0; i < max_links; i++) {
374                 as = shared_associations[i];
375                 if (persistent && as && !as->in_use &&
376                         !strcmp_null(option_get(as, "host"), zurl_str) &&
377                         !strcmp_null(option_get(as, "proxy"), proxy_str) &&
378                         !strcmp_null(option_get(as, "sru"), sru_str) &&
379                         !strcmp_null(option_get(as, "sru_version"), sru_version_str) &&
380                         !strcmp_null(option_get(as, "user"), user_str) &&
381                         !strcmp_null(option_get(as, "group"), group_str) &&
382                         !strcmp_null(option_get(as, "pass"), pass_str) &&
383                         !strcmp_null(option_get(as, "cookie"), cookie_str) &&
384                         !strcmp_null(option_get(as, "charset"), charset_str))
385                         break;
386         }
387         if (i == max_links) {
388                 /* we didn't have it (or already in use) */
389                 int i0 = -1;
390                 int min_order = 2000000000;
391
392                 /* find completely free slot or the oldest one */
393                 for (i = 0; i < max_links && shared_associations[i]; i++) {
394                         as = shared_associations[i];
395                         if (persistent && !as->in_use && as->order < min_order) {
396                                 min_order = as->order;
397                                 i0 = i;
398                         }
399                 }
400
401                 if (i == max_links) {
402                         i = i0;
403                         if (i == -1) {
404                                 char msg[80];
405 #ifdef ZTS
406                                 tsrm_mutex_unlock (yaz_mutex);
407 #endif
408                                 sprintf(msg, "No YAZ handles available. max_links=%d", 
409                                                 max_links);
410                                 php_error_docref(NULL TSRMLS_CC, E_WARNING,
411                                                                  "No YAZ handles available. max_links=%ld",
412                                                                  (long) max_links);
413                                 RETURN_LONG(0);                  /* no free slot */
414                         } else {                                         /* "best" free slot */
415                                 yaz_association_destroy(shared_associations[i]);
416                         }
417                 }
418                 shared_associations[i] = as = yaz_association_mk ();
419
420                 option_set(as, "proxy", proxy_str);
421                 option_set(as, "sru", sru_str);
422                 option_set(as, "sru_version", sru_version_str);
423                 option_set(as, "user", user_str);
424                 option_set(as, "group", group_str);
425                 option_set(as, "pass", pass_str);
426                 option_set(as, "cookie", cookie_str);
427                 option_set(as, "charset", charset_str);
428         }
429         if (maximumRecordSize)
430                 option_set(as, "maximumRecordSize", maximumRecordSize);
431         if (preferredMessageSize)
432                 option_set(as, "preferredMessageSize", preferredMessageSize);
433         option_set(as, "otherInfo0", otherInfo[0]);
434         option_set(as, "otherInfo1", otherInfo[1]);
435         option_set(as, "otherInfo2", otherInfo[2]);
436         option_set(as, "clientIP", client_IP);
437         option_set(as, "piggyback", piggyback ? "1" : "0");
438         option_set_int(as, "start", 0);
439         option_set_int(as, "count", 0);
440         ZOOM_connection_connect(as->zoom_conn, zurl_str, 0);
441         as->in_use = 1;
442         as->persistent = persistent;
443         as->order = YAZSG(assoc_seq);
444         as->time_stamp = time(0);
445
446         if (as->zoom_set)
447         {
448                 ZOOM_resultset_destroy(as->zoom_set);
449                 as->zoom_set = 0;
450         }
451 #ifdef ZTS
452         tsrm_mutex_unlock (yaz_mutex);
453 #endif
454
455         ZEND_REGISTER_RESOURCE(return_value, &shared_associations[i], le_link);
456         as->zval_resource = Z_LVAL_P(return_value);
457 }
458 /* }}} */
459
460 /* {{{ proto bool yaz_close(resource id)
461    Destory and close target */
462 PHP_FUNCTION(yaz_close)
463 {
464         Yaz_Association p;
465         pval *id;
466
467         if (ZEND_NUM_ARGS() != 1) {
468                 WRONG_PARAM_COUNT;
469         }
470         if (zend_parse_parameters(1, "z", &id) == FAILURE) {
471                 RETURN_FALSE;
472         }
473         get_assoc(INTERNAL_FUNCTION_PARAM_PASSTHRU, id, &p);
474         if (!p) {
475                 RETURN_FALSE;
476         }
477         release_assoc(p);
478         zend_list_delete(id->value.lval);
479
480         RETURN_TRUE;
481 }
482 /* }}} */
483
484 /* {{{ proto bool yaz_search(resource id, string type, string query)
485    Specify query of type for search - returns true if successful */
486 PHP_FUNCTION(yaz_search)
487 {
488         char *query_str, *type_str;
489         pval *id, *type, *query;
490         Yaz_Association p;
491
492         if (ZEND_NUM_ARGS() == 3) {
493                 if (zend_parse_parameters(3, "zzz", &id, &type, &query)
494                         == FAILURE) {
495                         WRONG_PARAM_COUNT;
496                 }
497         } else {
498                 WRONG_PARAM_COUNT;
499         }
500
501         get_assoc(INTERNAL_FUNCTION_PARAM_PASSTHRU, id, &p);
502         if (!p) {
503                 RETURN_FALSE;
504         }
505
506         convert_to_string_ex(&type);
507         type_str = type->value.str.val;
508         convert_to_string_ex(&query);
509         query_str = query->value.str.val;
510
511         ZOOM_resultset_destroy(p->zoom_set);
512         p->zoom_set = 0;
513
514         RETVAL_FALSE;
515
516         if (!strcmp(type_str, "rpn")) {
517                 ZOOM_query q = ZOOM_query_create();
518                 if (ZOOM_query_prefix(q, query_str) == 0)
519                 {
520                         if (p->sort_criteria) {
521                                 ZOOM_query_sortby(q, p->sort_criteria);
522                         }
523                         xfree(p->sort_criteria);
524                         p->sort_criteria = 0;
525                         p->zoom_set = ZOOM_connection_search(p->zoom_conn, q);
526                         RETVAL_TRUE;
527                 }
528                 ZOOM_query_destroy(q);
529         }
530         else if (!strcmp(type_str, "cql")) {
531                 ZOOM_query q = ZOOM_query_create();
532                 if (ZOOM_query_cql(q, query_str) == 0)
533                 {
534                         if (p->sort_criteria) {
535                                 ZOOM_query_sortby(q, p->sort_criteria);
536                         }
537                         xfree (p->sort_criteria);
538                         p->sort_criteria = 0;
539                         p->zoom_set = ZOOM_connection_search(p->zoom_conn, q);
540                         RETVAL_TRUE;
541                 }
542                 ZOOM_query_destroy(q);
543         }
544         else
545         {
546                 php_error_docref(NULL TSRMLS_CC, E_WARNING, 
547                                                  "Invalid query type %s", type_str);
548         }
549         release_assoc(p);
550 }
551 /* }}} */
552
553 /* {{{ proto bool yaz_present(resource id)
554    Retrieve records */
555 PHP_FUNCTION(yaz_present)
556 {
557         pval *id;
558         Yaz_Association p;
559
560         if (ZEND_NUM_ARGS() != 1) {
561                 WRONG_PARAM_COUNT;
562         }
563         if (zend_parse_parameters(1, "z", id) == FAILURE) {
564                 WRONG_PARAM_COUNT;
565         }
566
567         get_assoc(INTERNAL_FUNCTION_PARAM_PASSTHRU, id, &p);
568         if (!p) {
569                 RETURN_FALSE;
570         }
571
572         if (p->zoom_set) {
573                 size_t start = option_get_int(p, "start", 0);
574                 size_t count = option_get_int(p, "count", 0);
575                 if (count > 0) {
576                         ZOOM_resultset_records(p->zoom_set, 0 /* recs */, start, count);
577                 }
578         }
579         release_assoc(p);
580         RETURN_TRUE;
581 }
582 /* }}} */
583
584 /* {{{ proto bool yaz_wait([array options])
585    Process events. */
586 PHP_FUNCTION(yaz_wait)
587 {
588         pval *pval_options = 0;
589         int event_mode = 0;
590         int no = 0;
591         ZOOM_connection conn_ar[MAX_ASSOC];
592         Yaz_Association conn_as[MAX_ASSOC];
593         int i, timeout = 15;
594
595         if (ZEND_NUM_ARGS() == 1) {
596                 long *val = 0;
597                 long *event_bool = 0;
598                 HashTable *options_ht = 0;
599                 if (zend_parse_parameters(1, "z", &pval_options) == FAILURE) {
600                         WRONG_PARAM_COUNT;
601                 }
602                 if (Z_TYPE_PP(&pval_options) != IS_ARRAY) {
603                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "Expected array parameter");
604                         RETURN_FALSE;
605                 }
606                 options_ht = Z_ARRVAL_PP(&pval_options);
607                 val = array_lookup_long(options_ht, "timeout");
608                 if (val) {
609                         timeout = *val;
610                 }
611                 event_bool = array_lookup_bool(options_ht, "event");
612                 if (event_bool && *event_bool)
613                         event_mode = 1;
614         }
615 #ifdef ZTS
616         tsrm_mutex_lock(yaz_mutex);
617 #endif
618         for (i = 0; i<YAZSG(max_links); i++) {
619                 Yaz_Association p = shared_associations[i];
620                 if (p && p->order == YAZSG(assoc_seq)) {
621                         char str[20];
622
623                         sprintf(str, "%d", timeout);
624                         ZOOM_connection_option_set(p->zoom_conn, "timeout", str);
625                         conn_as[no] = p;
626                         conn_ar[no++] = p->zoom_conn;
627                 }
628         }
629 #ifdef ZTS
630         tsrm_mutex_unlock(yaz_mutex);
631 #endif
632         if (event_mode) {
633                 long ev = ZOOM_event(no, conn_ar);
634                 if (ev <= 0) {
635                         RETURN_FALSE;
636                 } else {
637                         Yaz_Association p = conn_as[ev-1];
638                         int event_code = ZOOM_connection_last_event(p->zoom_conn);
639
640                         add_assoc_long(pval_options, "connid", ev);
641
642                         add_assoc_long(pval_options, "eventcode", event_code);
643
644                         zend_list_addref(p->zval_resource);
645                         Z_LVAL_P(return_value) = p->zval_resource;
646                         Z_TYPE_P(return_value) = IS_RESOURCE;
647                         return;
648                 }
649         }
650
651         if (no) {
652                 while (ZOOM_event (no, conn_ar))
653                         ;
654         }
655         RETURN_TRUE;
656 }
657 /* }}} */
658
659 /* {{{ proto int yaz_errno(resource id)
660    Return last error number (>0 for bib-1 diagnostic, <0 for other error, 0 for no error */
661 PHP_FUNCTION(yaz_errno)
662 {
663         pval *id;
664         Yaz_Association p;
665
666         if (ZEND_NUM_ARGS() != 1 || 
667                 zend_parse_parameters(1, "z", &id) == FAILURE) {
668                 WRONG_PARAM_COUNT;
669         }
670         get_assoc(INTERNAL_FUNCTION_PARAM_PASSTHRU, id, &p);
671         if (!p) {
672                 RETURN_LONG(0);
673         }
674         RETVAL_LONG(ZOOM_connection_errcode(p->zoom_conn));
675         release_assoc(p);
676 }
677 /* }}} */
678
679 /* {{{ proto string yaz_error(resource id)
680    Return last error message */
681 PHP_FUNCTION(yaz_error)
682 {
683         pval *id;
684         Yaz_Association p;
685
686         if (ZEND_NUM_ARGS() != 1 ||
687                 zend_parse_parameters(1, "z", &id) == FAILURE) {
688                 WRONG_PARAM_COUNT;
689         }
690
691         get_assoc(INTERNAL_FUNCTION_PARAM_PASSTHRU, id, &p);
692         if (p) {
693                 int code = ZOOM_connection_errcode(p->zoom_conn);
694                 const char *msg = ZOOM_connection_errmsg(p->zoom_conn);
695
696                 if (!code) {
697                         msg = "";
698                 }
699                 return_value->value.str.len = strlen(msg);
700                 return_value->value.str.val = estrndup(msg, return_value->value.str.len);
701                 return_value->type = IS_STRING;
702         }
703         release_assoc(p);
704 }
705 /* }}} */
706
707 /* {{{ proto string yaz_addinfo(resource id)
708    Return additional info for last error (empty string if none) */
709 PHP_FUNCTION(yaz_addinfo)
710 {
711         pval *id;
712         Yaz_Association p;
713
714         if (ZEND_NUM_ARGS() != 1 ||
715                 zend_parse_parameters(ZEND_NUM_ARGS(), "z", &id) == FAILURE) {
716                 WRONG_PARAM_COUNT;
717         }
718
719         get_assoc(INTERNAL_FUNCTION_PARAM_PASSTHRU, id, &p);
720         if (p) {
721                 const char *addinfo = ZOOM_connection_addinfo(p->zoom_conn);
722
723                 return_value->value.str.len = strlen(addinfo);
724                 return_value->value.str.val = estrndup(addinfo, return_value->value.str.len);
725                 return_value->type = IS_STRING;
726         }                
727         release_assoc(p);
728 }
729 /* }}} */
730
731 /* {{{ proto int yaz_hits(resource id [, array searchresult])
732    Return number of hits (result count) for last search */
733 PHP_FUNCTION(yaz_hits)
734 {
735         pval *id, *searchresult = 0;
736         Yaz_Association p;
737
738         if (ZEND_NUM_ARGS() == 1) {
739                 if (zend_parse_parameters(1, "z", &id) == FAILURE) {
740                         WRONG_PARAM_COUNT;
741                 }
742         } else if (ZEND_NUM_ARGS() == 2) {
743                 if (zend_parse_parameters(2, "zz", &id, &searchresult) == FAILURE) {
744                         WRONG_PARAM_COUNT;
745                 }
746                 if (array_init(searchresult) == FAILURE) {
747                         php_error_docref(NULL TSRMLS_CC, E_WARNING,
748                                                          "Could not initialize search result array");
749                         RETURN_FALSE;
750                 }
751         } else {
752                 WRONG_PARAM_COUNT;
753         }
754
755         get_assoc(INTERNAL_FUNCTION_PARAM_PASSTHRU, id, &p);
756         if (p && p->zoom_set) {
757                 RETVAL_LONG(ZOOM_resultset_size(p->zoom_set));
758                 if (searchresult)
759                 {
760                         const char *str =
761                                 ZOOM_resultset_option_get(p->zoom_set, "resultSetStatus");
762                         if (str)
763                                 add_assoc_string(searchresult, "resultSetStatus", 
764                                                                  (char *) str, 1);
765                 }
766                 if (searchresult)
767                 {
768                         const char *sz_str =
769                                 ZOOM_resultset_option_get(p->zoom_set, "searchresult.size");
770                         int i, sz = 0;
771
772
773                         if (sz_str && *sz_str)
774                                 sz = atoi(sz_str);
775                         for (i = 0; i<sz; i++)
776                         {
777                                 char opt_name[80];
778                                 const char *opt_value;
779                                 zval *zval_element;
780
781                                 MAKE_STD_ZVAL(zval_element);
782                                 array_init(zval_element);
783                                 add_next_index_zval(searchresult, zval_element);
784                                 
785                                 sprintf(opt_name, "searchresult.%d.id", i);
786                                 opt_value = ZOOM_resultset_option_get(p->zoom_set, opt_name);
787                                 if (opt_value)
788                                         add_assoc_string(zval_element, "id",
789                                                                          (char *) opt_value, 1);
790
791                                 sprintf(opt_name, "searchresult.%d.count", i);
792                                 opt_value = ZOOM_resultset_option_get(p->zoom_set, opt_name);
793                                 if (opt_value)
794                                         add_assoc_long(zval_element, "count", atoi(opt_value));
795                                 
796                                 sprintf(opt_name, "searchresult.%d.subquery.term", i);
797                                 opt_value = ZOOM_resultset_option_get(p->zoom_set, opt_name);
798                                 if (opt_value)
799                                         add_assoc_string(zval_element, "subquery.term",
800                                                                          (char *) opt_value, 1);
801
802                                 sprintf(opt_name, "searchresult.%d.interpretation.term", i);
803                                 opt_value = ZOOM_resultset_option_get(p->zoom_set, opt_name);
804                                 if (opt_value)
805                                         add_assoc_string(zval_element, "interpretation.term",
806                                                                          (char *) opt_value, 1);
807
808                                 sprintf(opt_name, "searchresult.%d.recommendation.term", i);
809                                 opt_value = ZOOM_resultset_option_get(p->zoom_set, opt_name);
810                                 if (opt_value)
811                                         add_assoc_string(zval_element, "recommendation.term",
812                                                                          (char *) opt_value, 1);
813                         }
814                 }
815
816         } else {
817                 RETVAL_LONG(0);
818         }
819         release_assoc(p);
820 }
821 /* }}} */
822
823 static Z_GenericRecord *marc_to_grs1(const char *buf, ODR o)
824 {
825         int entry_p;
826         int record_length;
827         int indicator_length;
828         int identifier_length;
829         int base_address;
830         int length_data_entry;
831         int length_starting;
832         int length_implementation;
833         int max_elements = 256;
834         Z_GenericRecord *r = odr_malloc (o, sizeof(*r));
835         r->elements = odr_malloc (o, sizeof(*r->elements) * max_elements);
836         r->num_elements = 0;
837         
838         record_length = atoi_n(buf, 5);
839         if (record_length < 25) {
840                 return 0;
841         }
842         indicator_length = atoi_n(buf + 10, 1);
843         identifier_length = atoi_n(buf + 11, 1);
844         base_address = atoi_n(buf + 12, 5);
845         
846         length_data_entry = atoi_n(buf + 20, 1);
847         length_starting = atoi_n(buf + 21, 1);
848         length_implementation = atoi_n(buf + 22, 1);
849         
850         for (entry_p = 24; buf[entry_p] != ISO2709_FS; ) {
851                 entry_p += 3 + length_data_entry + length_starting;
852                 if (entry_p >= record_length) {
853                         return 0;
854                 }
855         }
856         if (1)
857         {
858                 Z_TaggedElement *tag;
859                 tag = r->elements[r->num_elements++] = odr_malloc(o, sizeof(*tag));
860                 tag->tagType = odr_malloc(o, sizeof(*tag->tagType));
861                 *tag->tagType = 3;
862                 tag->tagOccurrence = 0;
863                 tag->metaData = 0;
864                 tag->appliedVariant = 0;
865                 tag->tagValue = odr_malloc (o, sizeof(*tag->tagValue));
866                 tag->tagValue->which = Z_StringOrNumeric_string;
867                 tag->tagValue->u.string = odr_strdup(o, "leader");
868                 
869                 tag->content = odr_malloc(o, sizeof(*tag->content));
870                 tag->content->which = Z_ElementData_string;
871                 tag->content->u.string = odr_strdupn(o, buf, 24);
872         }
873         base_address = entry_p + 1;
874         for (entry_p = 24; buf[entry_p] != ISO2709_FS; ) {
875                 Z_TaggedElement *tag;
876                 int data_length;
877                 int data_offset;
878                 int end_offset;
879                 int i;
880                 char tag_str[4];
881                 int identifier_flag = 1;
882                 
883                 memcpy(tag_str, buf+entry_p, 3);
884                 entry_p += 3;
885                 tag_str[3] = '\0';
886                 
887                 if ((r->num_elements + 1) >= max_elements) {
888                         Z_TaggedElement **tmp = r->elements;
889                         
890                         /* double array space, throw away old buffer (nibble memory) */
891                         r->elements = odr_malloc(o, sizeof(*r->elements) * (max_elements *= 2));
892                         memcpy(r->elements, tmp, r->num_elements * sizeof(*tmp));
893                 }
894                 tag = r->elements[r->num_elements++] = odr_malloc(o, sizeof(*tag));
895                 tag->tagType = odr_malloc(o, sizeof(*tag->tagType));
896                 *tag->tagType = 3;
897                 tag->tagOccurrence = 0;
898                 tag->metaData = 0;
899                 tag->appliedVariant = 0;
900                 tag->tagValue = odr_malloc (o, sizeof(*tag->tagValue));
901                 tag->tagValue->which = Z_StringOrNumeric_string;
902                 tag->tagValue->u.string = odr_strdup(o, tag_str);
903                 
904                 tag->content = odr_malloc(o, sizeof(*tag->content));
905                 tag->content->which = Z_ElementData_subtree;
906                 
907                 tag->content->u.subtree = odr_malloc(o, sizeof(*tag->content->u.subtree));
908                 tag->content->u.subtree->elements = odr_malloc(o, sizeof(*r->elements));
909                 tag->content->u.subtree->num_elements = 1;
910                 
911                 tag = tag->content->u.subtree->elements[0] = odr_malloc(o, sizeof(**tag->content->u.subtree->elements));
912                 
913                 tag->tagType = odr_malloc(o, sizeof(*tag->tagType));
914                 *tag->tagType = 3;
915                 tag->tagOccurrence = 0;
916                 tag->metaData = 0;
917                 tag->appliedVariant = 0;
918                 tag->tagValue = odr_malloc(o, sizeof(*tag->tagValue));
919                 tag->tagValue->which = Z_StringOrNumeric_string;
920                 tag->content = odr_malloc(o, sizeof(*tag->content));
921                 
922                 data_length = atoi_n(buf + entry_p, length_data_entry);
923                 entry_p += length_data_entry;
924                 data_offset = atoi_n(buf + entry_p, length_starting);
925                 entry_p += length_starting;
926                 i = data_offset + base_address;
927                 end_offset = i + data_length - 1;
928
929                 if (indicator_length > 0 && indicator_length < 5) {
930                         if (buf[i + indicator_length] != ISO2709_IDFS) {
931                                 identifier_flag = 0;
932                         }
933                 } else if (!memcmp (tag_str, "00", 2)) {
934                         identifier_flag = 0;
935                 }
936                 
937                 if (identifier_flag && indicator_length) {
938                         /* indicator */
939                         tag->tagValue->u.string = odr_malloc(o, indicator_length + 1);
940                         memcpy(tag->tagValue->u.string, buf + i, indicator_length);
941                         tag->tagValue->u.string[indicator_length] = '\0';
942                         i += indicator_length;
943                         
944                         tag->content->which = Z_ElementData_subtree;
945
946                         tag->content->u.subtree = odr_malloc(o, sizeof(*tag->content->u.subtree));
947                         tag->content->u.subtree->elements = odr_malloc(o, 256 * sizeof(*r->elements));
948                         tag->content->u.subtree->num_elements = 0;
949                         
950                         while (buf[i] != ISO2709_RS && buf[i] != ISO2709_FS && i < end_offset) {
951                                 int i0;
952                                 /* prepare tag */
953                                 Z_TaggedElement *parent_tag = tag;
954                                 Z_TaggedElement *tag = odr_malloc (o, sizeof(*tag));
955                                 
956                                 if (parent_tag->content->u.subtree->num_elements < 256) {
957                                         parent_tag->content->u.subtree->elements[
958                                         parent_tag->content->u.subtree->num_elements++] = tag;
959                                 }
960                                                                 
961                                 tag->tagType = odr_malloc(o, sizeof(*tag->tagType));
962                                 *tag->tagType = 3;
963                                 tag->tagOccurrence = 0;
964                                 tag->metaData = 0;
965                                 tag->appliedVariant = 0;
966                                 tag->tagValue = odr_malloc (o, sizeof(*tag->tagValue));
967                                 tag->tagValue->which = Z_StringOrNumeric_string;
968                                 
969                                 /* sub field */
970                                 tag->tagValue->u.string = odr_malloc(o, identifier_length);
971                                 memcpy(tag->tagValue->u.string, buf + i + 1, identifier_length - 1);
972                                 tag->tagValue->u.string[identifier_length - 1] = '\0';
973                                 i += identifier_length;
974                                 
975                                 /* data ... */
976                                 tag->content = odr_malloc(o, sizeof(*tag->content));
977                                 tag->content->which = Z_ElementData_string;
978                                 
979                                 i0 = i;
980                                 while ( buf[i] != ISO2709_RS && 
981                                                 buf[i] != ISO2709_IDFS && 
982                                                 buf[i] != ISO2709_FS && i < end_offset) {
983                                         i++;
984                                 }
985                                                                 
986                                 tag->content->u.string = odr_malloc(o, i - i0 + 1);
987                                 memcpy(tag->content->u.string, buf + i0, i - i0);
988                                 tag->content->u.string[i - i0] = '\0';
989                         }
990                 } else {
991                         int i0 = i;
992                         
993                         tag->tagValue->u.string = "@";
994                         tag->content->which = Z_ElementData_string;
995                         
996                         while (buf[i] != ISO2709_RS && buf[i] != ISO2709_FS && i < end_offset) {
997                                 i++;
998                         }
999                         tag->content->u.string = odr_malloc(o, i - i0 +1);
1000                         memcpy(tag->content->u.string, buf + i0, i - i0);
1001                         tag->content->u.string[i-i0] = '\0';
1002                 }
1003         }
1004         return r;
1005 }
1006
1007 struct cvt_handle {
1008         ODR odr;
1009         yaz_iconv_t cd;
1010         char *buf;
1011         int size;
1012 };
1013
1014 static struct cvt_handle *cvt_open(const char *to, const char *from)
1015 {
1016         ODR o = odr_createmem(ODR_ENCODE);
1017
1018         struct cvt_handle *cvt = odr_malloc(o, sizeof(*cvt));
1019         cvt->odr = o;
1020         cvt->size = 10;
1021         cvt->buf = odr_malloc(o, cvt->size);
1022         cvt->cd = 0;
1023         if (to && from)
1024                 cvt->cd = yaz_iconv_open(to, from);
1025         return cvt;
1026 }
1027
1028 static void cvt_close(struct cvt_handle *cvt)
1029 {
1030         if (cvt->cd)
1031                 yaz_iconv_close(cvt->cd);
1032         odr_destroy(cvt->odr);
1033 }
1034
1035 static const char *cvt_string(const char *input, struct cvt_handle *cvt)
1036 {
1037         if (!cvt->cd)
1038                 return input;
1039         while(1) {
1040                 size_t inbytesleft = strlen(input);
1041                 const char *inp = input;
1042                 size_t outbytesleft = cvt->size - 1;
1043                 char *outp = cvt->buf;
1044                 size_t r = yaz_iconv(cvt->cd, (char**) &inp, &inbytesleft,
1045                                                          &outp, &outbytesleft);
1046                 if (r == (size_t) (-1)) {
1047                         int e = yaz_iconv_error(cvt->cd);
1048                         if (e != YAZ_ICONV_E2BIG || cvt->size > 200000)
1049                         {
1050                                 cvt->buf[0] = '\0';
1051                                 break;
1052                         }
1053                         cvt->size = cvt->size * 2 + 30;
1054                         cvt->buf = (char*) odr_malloc(cvt->odr, cvt->size);
1055                 } else {
1056                         cvt->buf[outp - cvt->buf] = '\0';
1057                         break;
1058                 }
1059         }
1060         return cvt->buf;
1061 }
1062
1063 static void retval_array3_grs1(zval *return_value, Z_GenericRecord *p,
1064                                                            struct cvt_handle *cvt)
1065 {
1066         int i;
1067         struct tag_list {
1068                 char *tag;
1069                 zval *zval_list;
1070                 struct tag_list *next;
1071         } *all_tags = 0;
1072         NMEM nmem = nmem_create();
1073
1074         array_init(return_value);
1075         for (i = 0; i<p->num_elements; i++)
1076         {
1077                 struct tag_list *tl;
1078                 zval *zval_element;
1079                 zval *zval_list;
1080                 Z_TaggedElement *e = p->elements[i];
1081                 char tagstr[32], *tag = 0;
1082                 
1083                 if (e->tagValue->which == Z_StringOrNumeric_numeric)
1084                 {
1085                         sprintf(tagstr, ODR_INT_PRINTF, *e->tagValue->u.numeric);
1086                         tag = tagstr;
1087                 }
1088                 else if (e->tagValue->which == Z_StringOrNumeric_string)
1089                         tag = e->tagValue->u.string, zval_element;
1090
1091                 if (!tag)
1092                         continue;
1093
1094                 for (tl = all_tags; tl; tl = tl->next)
1095                         if (!strcmp(tl->tag, tag))
1096                                 break;
1097                 if (tl)
1098                         zval_list = tl->zval_list;
1099                 else
1100                 {
1101                         MAKE_STD_ZVAL(zval_list);
1102                         array_init(zval_list);
1103                         add_assoc_zval(return_value, tag, zval_list);
1104                         
1105                         tl = nmem_malloc(nmem, sizeof(*tl));
1106                         tl->tag = nmem_strdup(nmem, tag);
1107                         tl->zval_list = zval_list;
1108                         tl->next = all_tags;
1109                         all_tags = tl;
1110                 }
1111                 MAKE_STD_ZVAL(zval_element);
1112                 array_init(zval_element);
1113                 add_next_index_zval(zval_list, zval_element);
1114                 if (e->content->which == Z_ElementData_subtree)
1115                 {
1116                         /* we have a subtree. Move to first child */
1117                         Z_GenericRecord *sub = e->content->u.subtree;
1118                         if (sub->num_elements >= 1)
1119                                 e = sub->elements[0];
1120                         else
1121                                 e = 0;
1122                 }
1123                 if (e)
1124                 {
1125                         const char *tag = 0;
1126                         if (e->tagValue->which == Z_StringOrNumeric_numeric)
1127                         {
1128                                 sprintf(tagstr, ODR_INT_PRINTF, *e->tagValue->u.numeric);
1129                                 tag = tagstr;
1130                         }
1131                         else if (e->tagValue->which == Z_StringOrNumeric_string)
1132                                 tag = e->tagValue->u.string;
1133                         if (tag && e->content->which == Z_ElementData_subtree)
1134                         {
1135                                 /* Data field */
1136                                 Z_GenericRecord *sub = e->content->u.subtree;
1137                                 int i;
1138                                 for (i = 0; tag[i]; i++)
1139                                 {
1140                                         char ind_idx[5];
1141                                         char ind_val[2];
1142                                         
1143                                         sprintf(ind_idx, "ind%d", i+1);
1144                                         ind_val[0] = tag[i];
1145                                         ind_val[1] = '\0';
1146                                         
1147                                         add_assoc_string(zval_element, ind_idx, ind_val, 1);
1148                                 }
1149                                 for (i = 0; i<sub->num_elements; i++)
1150                                 {
1151                                         Z_TaggedElement *e = sub->elements[i];
1152                                         const char *tag = 0;
1153                                         if (e->tagValue->which == Z_StringOrNumeric_numeric)
1154                                         {
1155                                                 sprintf(tagstr, ODR_INT_PRINTF, *e->tagValue->u.numeric);
1156                                                 tag = tagstr;
1157                                         }
1158                                         else if (e->tagValue->which == Z_StringOrNumeric_string)
1159                                                 tag = e->tagValue->u.string, zval_element;
1160                                         
1161                                         if (tag && e->content->which == Z_ElementData_string)
1162                                         {
1163                                                 const char *v = cvt_string(e->content->u.string, cvt);
1164                                                 add_assoc_string(zval_element, (char*) tag, (char*) v, 
1165                                                                                  1);
1166                                         }
1167                                 }
1168                         }
1169                         else if (tag && e->content->which == Z_ElementData_string)
1170                         {
1171                                 /* Leader or control field */
1172                                 const char *v = cvt_string(e->content->u.string, cvt);
1173                                 ZVAL_STRING(zval_element, (char*) v, 1);
1174                         }
1175                 }
1176         }
1177         nmem_destroy(nmem);
1178 }
1179
1180 static void retval_array2_grs1(zval *return_value, Z_GenericRecord *p,
1181                                                            struct cvt_handle *cvt)
1182 {
1183         int i;
1184         
1185         array_init(return_value);
1186         
1187         for (i = 0; i<p->num_elements; i++)
1188         {
1189                 zval *zval_element;
1190                 zval *zval_sub;
1191                 Z_TaggedElement *e = p->elements[i];
1192                 
1193                 MAKE_STD_ZVAL(zval_element);
1194                 array_init(zval_element);
1195                 
1196                 if (e->tagType)
1197                         add_assoc_long(zval_element, "tagType", *e->tagType);
1198
1199                 if (e->tagValue->which == Z_StringOrNumeric_string)
1200                         add_assoc_string(zval_element, "tag", e->tagValue->u.string, 1);
1201                 else if (e->tagValue->which == Z_StringOrNumeric_numeric)
1202                         add_assoc_long(zval_element, "tag", *e->tagValue->u.numeric);
1203
1204                 switch (e->content->which) {
1205                 case Z_ElementData_string:
1206                         if (1)
1207                         {
1208                                 const char *v = cvt_string(e->content->u.string, cvt);
1209                                 add_assoc_string(zval_element, "content", (char*) v, 1);
1210                         }
1211                         break;
1212                 case Z_ElementData_numeric:
1213                         add_assoc_long(zval_element, "content",*e->content->u.numeric);
1214                         break;
1215                 case Z_ElementData_trueOrFalse:
1216                         add_assoc_bool(zval_element, "content",*e->content->u.trueOrFalse);
1217                         break;
1218                 case Z_ElementData_subtree:
1219                         MAKE_STD_ZVAL(zval_sub);
1220                         retval_array2_grs1(zval_sub, e->content->u.subtree, cvt);
1221                         add_assoc_zval(zval_element, "content", zval_sub);
1222                 }
1223                 add_next_index_zval(return_value, zval_element);
1224         }
1225 }
1226
1227 static void retval_array1_grs1(zval *return_value, Z_GenericRecord *p,
1228                                                            struct cvt_handle *cvt)
1229 {
1230         Z_GenericRecord *grs[20];
1231         int eno[20];
1232         int level = 0;
1233
1234         array_init(return_value);
1235         eno[level] = 0;
1236         grs[level] = p;
1237
1238         while (level >= 0) {
1239                 zval *my_zval;
1240                 Z_TaggedElement *e = 0;
1241                 Z_GenericRecord *p = grs[level];
1242                 int i;
1243                 char tag[256];
1244                 int taglen = 0;
1245
1246                 if (eno[level] >= p->num_elements) {
1247                         --level;
1248                         if (level >= 0)
1249                                 eno[level]++;
1250                         continue;
1251                 }
1252                 *tag = '\0';
1253                 for (i = 0; i <= level; i++) {
1254                         int tag_type = 3;
1255                         e = grs[i]->elements[eno[i]];
1256
1257                         if (e->tagType) {
1258                                 tag_type = *e->tagType;
1259                         }
1260                         taglen = strlen(tag);
1261                         sprintf(tag + taglen, "(%d,", tag_type);
1262                         taglen = strlen(tag);
1263
1264                         if (e->tagValue->which == Z_StringOrNumeric_string) {
1265                                 int len = strlen(e->tagValue->u.string);
1266
1267                                 memcpy(tag + taglen, e->tagValue->u.string, len);
1268                                 tag[taglen+len] = '\0';
1269                         } else if (e->tagValue->which == Z_StringOrNumeric_numeric) {
1270                                 sprintf(tag + taglen, ODR_INT_PRINTF, *e->tagValue->u.numeric);
1271                         }
1272                         taglen = strlen(tag);
1273                         strcpy(tag + taglen, ")");
1274                 }
1275
1276                 ALLOC_ZVAL(my_zval);
1277                 array_init(my_zval);
1278                 INIT_PZVAL(my_zval);
1279                 
1280                 add_next_index_string(my_zval, tag, 1);
1281
1282                 switch (e->content->which) {
1283                         case Z_ElementData_string:
1284                                 if (1)
1285                                 {
1286                                         const char *v = cvt_string(e->content->u.string, cvt);
1287                                         add_next_index_string(my_zval, (char*) v, 1);
1288                                 }
1289                                 break;
1290                         case Z_ElementData_numeric:
1291                                 add_next_index_long(my_zval, *e->content->u.numeric);
1292                                 break;
1293                         case Z_ElementData_trueOrFalse:
1294                                 add_next_index_long(my_zval, *e->content->u.trueOrFalse);
1295                                 break;
1296                         case Z_ElementData_subtree:
1297                                 if (level < 20) {
1298                                         level++;
1299                                         grs[level] = e->content->u.subtree;
1300                                         eno[level] = -1;
1301                                 }
1302                 }
1303                 zend_hash_next_index_insert(return_value->value.ht, (void *) &my_zval, sizeof(zval *), NULL);
1304                 eno[level]++;
1305         }
1306 }
1307
1308 static void ext_grs1(zval *return_value, char type_args[][60],
1309                                          ZOOM_record r,
1310                                          void (*array_func)(zval *, Z_GenericRecord *,
1311                                                                                 struct cvt_handle *))
1312 {
1313         Z_External *ext = (Z_External *) ZOOM_record_get(r, "ext", 0);
1314         if (ext && ext->which == Z_External_OPAC)
1315                 ext = ext->u.opac->bibliographicRecord;
1316         if (ext) {
1317                 struct cvt_handle *cvt = 0;
1318                 if (type_args[2][0])
1319                         cvt = cvt_open(type_args[3], type_args[2]);
1320                 else
1321                         cvt = cvt_open(0, 0);
1322                 
1323                 if (ext->which == Z_External_grs1) {
1324                         retval_array1_grs1(return_value, ext->u.grs1, cvt);
1325                 } else if (ext->which == Z_External_octet) {
1326                         Z_GenericRecord *rec = 0;
1327                         if (yaz_oid_is_iso2709(ext->direct_reference))
1328                         {
1329                                 char *buf = (char *) (ext->u.octet_aligned->buf);
1330                                 rec = marc_to_grs1(buf, cvt->odr);
1331                         }
1332                         if (rec) {
1333                                 (*array_func)(return_value, rec, cvt);
1334                         }
1335                 }
1336                 cvt_close(cvt);
1337         }
1338 }
1339
1340
1341 /* {{{ proto string yaz_record(resource id, int pos, string type)
1342    Return record information at given result set position */
1343 PHP_FUNCTION(yaz_record)
1344 {
1345         pval *pval_id, *pval_pos, *pval_type;
1346         Yaz_Association p;
1347         int pos;
1348         char *type;
1349
1350         if (ZEND_NUM_ARGS() != 3) {
1351                 WRONG_PARAM_COUNT;
1352         }
1353         if (zend_parse_parameters(3, "zzz", &pval_id, &pval_pos, &pval_type)
1354                 == FAILURE) {
1355                 WRONG_PARAM_COUNT;
1356         }
1357
1358         get_assoc(INTERNAL_FUNCTION_PARAM_PASSTHRU, pval_id, &p);
1359
1360         convert_to_long_ex(&pval_pos);
1361         pos = pval_pos->value.lval;
1362         convert_to_string_ex(&pval_type);
1363         type = pval_type->value.str.val;
1364
1365         if (p && p->zoom_set) {
1366                 ZOOM_record r;
1367                 char type_args[4][60];  /*  0; 1=2,3  (1 is assumed charset) */
1368                 type_args[0][0] = 0;
1369                 type_args[1][0] = 0;
1370                 type_args[2][0] = 0;
1371                 type_args[3][0] = 0;
1372                 sscanf(type, "%59[^;];%59[^=]=%59[^,],%59[^,]", type_args[0],
1373                            type_args[1], type_args[2], type_args[3]);
1374                 r = ZOOM_resultset_record(p->zoom_set, pos-1);
1375                 if (!strcmp(type_args[0], "string")) {
1376                         type = "render";
1377                 }
1378                 if (r) {
1379                         if (!strcmp(type_args[0], "array") || 
1380                                 !strcmp(type_args[0], "array1"))
1381                         {
1382                                 ext_grs1(return_value, type_args, r, retval_array1_grs1);
1383                         } else if (!strcmp(type_args[0], "array2")) {
1384                                 ext_grs1(return_value, type_args, r, retval_array2_grs1);
1385                         } else if (!strcmp(type_args[0], "array3")) {
1386                                 ext_grs1(return_value, type_args, r, retval_array3_grs1);
1387                         } else {
1388                                 int rlen;
1389                                 const char *info = ZOOM_record_get(r, type, &rlen);
1390                                 if (info) {
1391                                         return_value->value.str.len = (rlen > 0) ? rlen : 0;
1392                                         return_value->value.str.val =
1393                                                 estrndup(info, return_value->value.str.len);
1394                                         return_value->type = IS_STRING;
1395                                 }
1396                                 else
1397                                 {
1398                                         php_error_docref(NULL TSRMLS_CC, E_WARNING, 
1399                                                                          "Bad yaz_record type %s - or unable "
1400                                                                          "to return record with type given", type);
1401                                 }
1402                         }
1403                 }
1404         }
1405         release_assoc (p);
1406 }
1407 /* }}} */
1408
1409 /* {{{ proto void yaz_syntax(resource id, string syntax)
1410    Set record syntax for retrieval */
1411 PHP_FUNCTION(yaz_syntax)
1412 {
1413         pval *pval_id, *pval_syntax;
1414         Yaz_Association p;
1415
1416         if (ZEND_NUM_ARGS() != 2 ||
1417                 zend_parse_parameters(2, "zz", &pval_id, &pval_syntax) == FAILURE) {
1418                 WRONG_PARAM_COUNT;
1419         }
1420
1421         get_assoc(INTERNAL_FUNCTION_PARAM_PASSTHRU, pval_id, &p);
1422         convert_to_string_ex(&pval_syntax);
1423         option_set(p, "preferredRecordSyntax", pval_syntax->value.str.val);
1424         release_assoc(p);
1425 }
1426 /* }}} */
1427
1428 /* {{{ proto void yaz_element(resource id, string elementsetname)
1429    Set Element-Set-Name for retrieval */
1430 PHP_FUNCTION(yaz_element)
1431 {
1432         pval *pval_id, *pval_element;
1433         Yaz_Association p;
1434
1435         if (ZEND_NUM_ARGS() != 2 ||
1436                 zend_parse_parameters(2, "zz", &pval_id, &pval_element) == FAILURE) {
1437                 WRONG_PARAM_COUNT;
1438         }
1439
1440         get_assoc(INTERNAL_FUNCTION_PARAM_PASSTHRU, pval_id, &p);
1441
1442         convert_to_string_ex(&pval_element);
1443         option_set(p, "elementSetName", pval_element->value.str.val);
1444         release_assoc(p);
1445 }
1446 /* }}} */
1447
1448 /* {{{ proto void yaz_schema(resource id, string schema)
1449    Set Schema for retrieval */
1450 PHP_FUNCTION(yaz_schema)
1451 {
1452         pval *pval_id, *pval_element;
1453         Yaz_Association p;
1454
1455         if (ZEND_NUM_ARGS() != 2 ||
1456                 zend_parse_parameters(2, "zz", &pval_id, &pval_element) == FAILURE) {
1457                 WRONG_PARAM_COUNT;
1458         }
1459
1460         get_assoc(INTERNAL_FUNCTION_PARAM_PASSTHRU, pval_id, &p);
1461         convert_to_string_ex(&pval_element);
1462         option_set(p, "schema", pval_element->value.str.val);
1463         release_assoc(p);
1464 }
1465 /* }}} */
1466
1467 /* {{{ proto void yaz_set_option(resource id, mixed options)
1468    Set Option(s) for connection */
1469 PHP_FUNCTION(yaz_set_option)
1470 {
1471         pval *pval_ar, *pval_name, *pval_val, *pval_id;
1472         Yaz_Association p;
1473
1474         if (ZEND_NUM_ARGS() == 2) {
1475                 if (zend_parse_parameters(2, "zz", &pval_id, &pval_ar) == FAILURE) {
1476                         WRONG_PARAM_COUNT;
1477                 }
1478                 if (Z_TYPE_PP(&pval_ar) != IS_ARRAY) {
1479                         WRONG_PARAM_COUNT;
1480                 }
1481                 get_assoc(INTERNAL_FUNCTION_PARAM_PASSTHRU, pval_id, &p);
1482                 if (p) {
1483                         HashPosition pos;
1484                         HashTable *ht;
1485                         zval **ent;
1486                         
1487                         ht = Z_ARRVAL_PP(&pval_ar);
1488                         for(zend_hash_internal_pointer_reset_ex(ht, &pos);
1489                                 zend_hash_get_current_data_ex(ht, (void**) &ent, &pos) == SUCCESS;
1490                                 zend_hash_move_forward_ex(ht, &pos)
1491                         ) {
1492                                 char *key;
1493                                 ulong idx;
1494 #if PHP_API_VERSION > 20010101
1495                                 int type = zend_hash_get_current_key_ex(ht, &key, 0, &idx, 0, &pos);
1496 #else
1497                                 int type = zend_hash_get_current_key_ex(ht, &key, 0, &idx, &pos);
1498 #endif
1499                                 if (type != HASH_KEY_IS_STRING || Z_TYPE_PP(ent) != IS_STRING) {
1500                                         continue;
1501                                 }
1502                                 option_set(p, key, (*ent)->value.str.val);
1503                         }
1504                         release_assoc (p);
1505                 }
1506         } else if (ZEND_NUM_ARGS() == 3) {
1507                 if (zend_parse_parameters(3, "zzz", &pval_id, &pval_name, &pval_val)
1508                         == FAILURE) {
1509                         WRONG_PARAM_COUNT;
1510                 }
1511                 get_assoc (INTERNAL_FUNCTION_PARAM_PASSTHRU, pval_id, &p);
1512                 convert_to_string_ex(&pval_name);
1513                 convert_to_string_ex(&pval_val);
1514                 option_set(p, pval_name->value.str.val, pval_val->value.str.val);
1515                 
1516                 release_assoc(p);
1517         } else {
1518                 WRONG_PARAM_COUNT;
1519         }
1520 }
1521 /* }}} */
1522
1523 /* {{{ proto string yaz_get_option(resource id, string name)
1524    Set Option(s) for connection */
1525 PHP_FUNCTION(yaz_get_option)
1526 {
1527         pval *pval_id, *pval_name;
1528         Yaz_Association p;
1529
1530         if (ZEND_NUM_ARGS() != 2) {
1531                 WRONG_PARAM_COUNT;
1532         }
1533         if (zend_parse_parameters(2, "zz", &pval_id, &pval_name) == FAILURE) {
1534                 WRONG_PARAM_COUNT;
1535         }
1536
1537         get_assoc(INTERNAL_FUNCTION_PARAM_PASSTHRU, pval_id, &p);
1538         if (p) {
1539                 const char *name_str, *v;
1540                 convert_to_string_ex(&pval_name);
1541                 name_str = pval_name->value.str.val;
1542
1543                 v = option_get(p, name_str);
1544                 if (!v) {
1545                         v = "";
1546                 }
1547                 return_value->value.str.len = strlen(v);
1548                 return_value->value.str.val = estrndup(v, return_value->value.str.len);
1549                 return_value->type = IS_STRING;
1550         } else {
1551                 RETVAL_FALSE;
1552         }
1553         release_assoc(p);
1554 }
1555 /* }}} */
1556
1557 /* {{{ proto void yaz_range(resource id, int start, int number)
1558    Set result set start point and number of records to request */
1559 PHP_FUNCTION(yaz_range)
1560 {
1561         pval *pval_id, *pval_start, *pval_number;
1562         Yaz_Association p;
1563
1564         if (ZEND_NUM_ARGS() != 3 ||
1565                 zend_parse_parameters(3, "zzz", &pval_id, &pval_start, &pval_number)
1566                 == FAILURE) {
1567                 WRONG_PARAM_COUNT;
1568         }
1569
1570         get_assoc(INTERNAL_FUNCTION_PARAM_PASSTHRU, pval_id, &p);
1571         convert_to_long_ex(&pval_start);
1572         convert_to_long_ex(&pval_number);
1573         option_set_int(p, "start", pval_start->value.lval - 1);
1574         option_set_int(p, "count", pval_number->value.lval);
1575         release_assoc(p);
1576 }
1577 /* }}} */
1578
1579 /* {{{ proto void yaz_sort(resource id, string sortspec)
1580    Set result set sorting criteria */
1581 PHP_FUNCTION(yaz_sort)
1582 {
1583         pval *pval_id, *pval_criteria;
1584         Yaz_Association p;
1585
1586         if (ZEND_NUM_ARGS() != 2 ||
1587                 zend_parse_parameters(2, "zz", &pval_id, &pval_criteria) == FAILURE) {
1588                 WRONG_PARAM_COUNT;
1589         }
1590
1591         get_assoc(INTERNAL_FUNCTION_PARAM_PASSTHRU, pval_id, &p);
1592         if (p) {
1593                 convert_to_string_ex(&pval_criteria);
1594                 xfree(p->sort_criteria);
1595                 p->sort_criteria = xstrdup(pval_criteria->value.str.val);
1596                 if (p->zoom_set)
1597                         ZOOM_resultset_sort(p->zoom_set, "yaz",
1598                                                                 pval_criteria->value.str.val);
1599         }
1600         release_assoc(p);
1601 }
1602 /* }}} */
1603
1604 const char *ill_array_lookup (void *handle, const char *name)
1605 {
1606         return array_lookup_string((HashTable *) handle, name);
1607 }
1608
1609 /* {{{ proto void yaz_itemorder(resource id, array package)
1610    Sends Item Order request */
1611 PHP_FUNCTION(yaz_itemorder)
1612 {
1613         pval *pval_id, *pval_package;
1614         Yaz_Association p;
1615
1616         if (ZEND_NUM_ARGS() != 2 ||
1617                 zend_parse_parameters(2, "zz", &pval_id, &pval_package) == FAILURE) {
1618                 WRONG_PARAM_COUNT;
1619         }
1620         if (Z_TYPE_PP(&pval_package) != IS_ARRAY) {
1621                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Expected array parameter");
1622                 RETURN_FALSE;
1623         }
1624
1625         get_assoc(INTERNAL_FUNCTION_PARAM_PASSTHRU, pval_id, &p);
1626         if (p) {
1627                 ZOOM_options options = ZOOM_options_create();
1628                 
1629                 ZOOM_options_set_callback(options,
1630                                                                   ill_array_lookup, Z_ARRVAL_PP(&pval_package));
1631                 ZOOM_package_destroy(p->zoom_package);
1632                 p->zoom_package = ZOOM_connection_package(p->zoom_conn, options);
1633                 ZOOM_package_send(p->zoom_package, "itemorder");
1634                 ZOOM_options_set_callback(options, 0, 0);
1635                 ZOOM_options_destroy (options);
1636         }
1637         release_assoc(p);
1638 }
1639 /* }}} */
1640
1641 /* {{{ proto void yaz_es(resource id, string type, array package)
1642    Sends Extended Services Request */
1643 PHP_FUNCTION(yaz_es)
1644 {
1645         pval *pval_id, *pval_type, *pval_package;
1646         Yaz_Association p;
1647         
1648         if (ZEND_NUM_ARGS() != 3 ||
1649                 zend_parse_parameters(3, "zzz", &pval_id, &pval_type, 
1650                                                           &pval_package) == FAILURE) {
1651                 WRONG_PARAM_COUNT;
1652         }
1653         if (Z_TYPE_PP(&pval_type) != IS_STRING) {
1654                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Expected string parameter");
1655                 RETURN_FALSE;
1656         }
1657         
1658         if (Z_TYPE_PP(&pval_package) != IS_ARRAY) {
1659                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Expected array parameter");
1660                 RETURN_FALSE;
1661         }
1662
1663         get_assoc(INTERNAL_FUNCTION_PARAM_PASSTHRU, pval_id, &p);
1664         if (p) {
1665                 ZOOM_options options = ZOOM_options_create();
1666                 
1667                 ZOOM_options_set_callback(options, ill_array_lookup,
1668                                                                   Z_ARRVAL_PP(&pval_package));
1669                 ZOOM_package_destroy(p->zoom_package);
1670                 p->zoom_package = ZOOM_connection_package(p->zoom_conn, options);
1671                 ZOOM_package_send(p->zoom_package, pval_type->value.str.val);
1672                 ZOOM_options_set_callback(options, 0, 0);
1673                 ZOOM_options_destroy (options);
1674         }
1675         release_assoc(p);
1676 }
1677 /* }}} */
1678
1679 /* {{{ proto void yaz_scan(resource id, type, query [, flags])
1680    Sends Scan Request */
1681 PHP_FUNCTION(yaz_scan)
1682 {
1683         pval *pval_id, *pval_type, *pval_query, *pval_flags = 0;
1684         HashTable *flags_ht = 0;
1685         Yaz_Association p;
1686
1687         if (ZEND_NUM_ARGS() == 3) {
1688                 if (zend_parse_parameters(3, "zzz", &pval_id, &pval_type, &pval_query)
1689                         == FAILURE) {
1690                         WRONG_PARAM_COUNT;
1691                 }
1692         } else if (ZEND_NUM_ARGS() == 4) {
1693                 if (zend_parse_parameters(4, "zzzz", &pval_id, &pval_type,
1694                                                                   &pval_query, &pval_flags) == FAILURE) {
1695                         WRONG_PARAM_COUNT;
1696                 }
1697                 if (Z_TYPE_PP(&pval_flags) != IS_ARRAY) {
1698                         php_error_docref(NULL TSRMLS_CC, E_WARNING, "Bad flags parameter");
1699                         RETURN_FALSE;
1700                 }
1701                 flags_ht = Z_ARRVAL_PP(&pval_flags);
1702         } else {
1703                 WRONG_PARAM_COUNT;
1704         }
1705
1706         convert_to_string_ex(&pval_type);
1707         convert_to_string_ex(&pval_query);
1708
1709         get_assoc(INTERNAL_FUNCTION_PARAM_PASSTHRU, pval_id, &p);
1710         ZOOM_scanset_destroy(p->zoom_scan);
1711         p->zoom_scan = 0;
1712         if (p) {
1713                 option_set(p, "number", array_lookup_string(flags_ht, "number"));
1714                 option_set(p, "position", array_lookup_string(flags_ht, "position"));
1715                 option_set(p, "stepSize", array_lookup_string(flags_ht, "stepsize"));
1716                 p->zoom_scan = ZOOM_connection_scan(p->zoom_conn, 
1717                                                                                         Z_STRVAL_PP(&pval_query));
1718         }
1719         release_assoc(p);
1720 }
1721 /* }}} */
1722
1723 /* {{{ proto array yaz_es_result(resource id)
1724    Inspects Extended Services Result */
1725 PHP_FUNCTION(yaz_es_result)
1726 {
1727         pval *pval_id;
1728         Yaz_Association p;
1729
1730         if (ZEND_NUM_ARGS() != 1 ||
1731                 zend_parse_parameters(1, "z", &pval_id) == FAILURE) {
1732                 WRONG_PARAM_COUNT;
1733         }
1734
1735         array_init(return_value);
1736
1737         get_assoc(INTERNAL_FUNCTION_PARAM_PASSTHRU, pval_id, &p);
1738         if (p && p->zoom_package) {
1739                 const char *str = ZOOM_package_option_get(p->zoom_package, 
1740                                                                                                   "targetReference");
1741                 
1742                 if (str) {
1743                         add_assoc_string(return_value, "targetReference", (char *) str, 1);
1744                 }
1745                 str = ZOOM_package_option_get(p->zoom_package, 
1746                                                                           "xmlUpdateDoc");
1747                 if (str) {
1748                         add_assoc_string(return_value, "xmlUpdateDoc", (char *) str, 1);
1749                 }
1750         }
1751         release_assoc(p);
1752 }
1753 /* }}} */
1754
1755 /* {{{ proto array yaz_scan_result(resource id [, array options])
1756    Inspects Scan Result */
1757 PHP_FUNCTION(yaz_scan_result)
1758 {
1759         pval *pval_id, *pval_opt = 0;
1760         Yaz_Association p;
1761
1762         if (ZEND_NUM_ARGS() == 2) {
1763                 if (zend_parse_parameters(2, "zz", &pval_id, &pval_opt) == FAILURE) {
1764                         WRONG_PARAM_COUNT;
1765                 }
1766         } else if (ZEND_NUM_ARGS() == 1) {
1767                 if (zend_parse_parameters(1, "z", &pval_id) == FAILURE) {
1768                         WRONG_PARAM_COUNT;
1769                 }
1770         } else {
1771                 WRONG_PARAM_COUNT;
1772         }
1773
1774         array_init(return_value);
1775
1776         if (pval_opt && array_init(pval_opt) == FAILURE) {
1777                 RETURN_FALSE;
1778         }
1779
1780         get_assoc(INTERNAL_FUNCTION_PARAM_PASSTHRU, pval_id, &p);
1781         if (p && p->zoom_scan) {
1782                 int pos = 0;
1783                 /* ZOOM_scanset_term changed from YAZ 3 to YAZ 4 */
1784 #if YAZ_VERSIONL >= 0x040000
1785                 size_t occ, len;
1786 #else
1787                 int occ, len;
1788 #endif
1789                 int size = ZOOM_scanset_size(p->zoom_scan);
1790                 
1791                 for (pos = 0; pos < size; pos++) {
1792                         const char *term = ZOOM_scanset_term(p->zoom_scan, pos, &occ, &len);
1793                         zval *my_zval;
1794
1795                         ALLOC_ZVAL(my_zval);
1796                         array_init(my_zval);
1797                         INIT_PZVAL(my_zval);
1798                         
1799                         add_next_index_string(my_zval, "term", 1);
1800
1801                         if (term) {
1802                                 add_next_index_stringl(my_zval, (char*) term, len, 1);
1803                         } else {
1804                                 add_next_index_string(my_zval, "?", 1);
1805                         }
1806                         add_next_index_long(my_zval, occ);
1807
1808                         term = ZOOM_scanset_display_term(p->zoom_scan, pos, &occ, &len);
1809
1810                         if (term) {
1811                                 add_next_index_stringl(my_zval, (char*) term, len, 1);
1812                         } else {
1813                                 add_next_index_string(my_zval, "?", 1);
1814                         }
1815
1816                         zend_hash_next_index_insert(return_value->value.ht, (void *) &my_zval, sizeof(zval *), NULL);
1817                 }
1818
1819                 if (pval_opt) {
1820                         const char *v;
1821         
1822                         add_assoc_long(pval_opt, "number", size);
1823                         
1824                         v = ZOOM_scanset_option_get(p->zoom_scan, "stepSize");
1825                         if (v) {
1826                                 add_assoc_long(pval_opt, "stepsize", atoi(v));
1827                         }
1828                         v = ZOOM_scanset_option_get(p->zoom_scan, "position");
1829                         if (v) {
1830                                 add_assoc_long(pval_opt, "position", atoi(v));
1831                         }
1832                         v = ZOOM_scanset_option_get(p->zoom_scan, "scanStatus");
1833                         if (v) {
1834                                 add_assoc_long(pval_opt, "status", atoi(v));
1835                         }
1836                 }
1837         }
1838         release_assoc(p);
1839 }
1840 /* }}} */
1841
1842 /* {{{ proto void yaz_ccl_conf(resource id, array package)
1843    Configure CCL package */
1844 PHP_FUNCTION(yaz_ccl_conf)
1845 {
1846         pval *pval_id, *pval_package;
1847         Yaz_Association p;
1848
1849         if (ZEND_NUM_ARGS() != 2 ||
1850                 zend_parse_parameters(2, "zz", &pval_id, &pval_package) == FAILURE) {
1851                 WRONG_PARAM_COUNT;
1852         }
1853
1854         if (Z_TYPE_PP(&pval_package) != IS_ARRAY) {
1855                 php_error_docref(NULL TSRMLS_CC, E_WARNING, "Expected array parameter");
1856                 RETURN_FALSE;
1857         }
1858
1859         get_assoc(INTERNAL_FUNCTION_PARAM_PASSTHRU, pval_id, &p);
1860         if (p) {
1861                 HashTable *ht = Z_ARRVAL_PP(&pval_package);
1862                 HashPosition pos;
1863                 zval **ent;
1864                 char *key;
1865
1866                 ccl_qual_rm(&p->bibset);
1867                 p->bibset = ccl_qual_mk();
1868
1869                 for(zend_hash_internal_pointer_reset_ex(ht, &pos);
1870                         zend_hash_get_current_data_ex(ht, (void**) &ent, &pos) == SUCCESS;
1871                         zend_hash_move_forward_ex(ht, &pos)
1872                 ) {
1873                         ulong idx;
1874 #if PHP_API_VERSION > 20010101
1875                         int type = zend_hash_get_current_key_ex(ht, &key, 0, &idx, 0, &pos);
1876 #else
1877                         int type = zend_hash_get_current_key_ex(ht, &key, 0, &idx, &pos);
1878 #endif
1879                         if (type != HASH_KEY_IS_STRING || Z_TYPE_PP(ent) != IS_STRING) {
1880                                 continue;
1881                         }
1882                         ccl_qual_fitem(p->bibset, (*ent)->value.str.val, key);
1883                 }
1884         }
1885         release_assoc (p);
1886 }
1887 /* }}} */
1888
1889 /* {{{ proto bool yaz_ccl_parse(resource id, string query, array res)
1890    Parse a CCL query */
1891 PHP_FUNCTION(yaz_ccl_parse)
1892 {
1893         pval *pval_id, *pval_query, *pval_res = 0;
1894         Yaz_Association p;
1895
1896         if (ZEND_NUM_ARGS() != 3 ||
1897                 zend_parse_parameters(3, "zzz", &pval_id, &pval_query, &pval_res)
1898                 == FAILURE) {
1899                 WRONG_PARAM_COUNT;
1900         }
1901         
1902         pval_destructor(pval_res);
1903         array_init(pval_res);
1904         convert_to_string_ex(&pval_query);
1905
1906         get_assoc(INTERNAL_FUNCTION_PARAM_PASSTHRU, pval_id, &p);
1907         if (p) {
1908                 const char *query_str = pval_query->value.str.val;
1909                 struct ccl_rpn_node *rpn;
1910                 int error_pos;
1911                 int error_code;
1912                 CCL_parser ccl_parser = ccl_parser_create(p->bibset);
1913
1914                 rpn = ccl_parser_find_str(ccl_parser, query_str);
1915
1916                 error_code = ccl_parser_get_error(ccl_parser, &error_pos);
1917                 add_assoc_long(pval_res, "errorcode", error_code);
1918
1919                 if (error_code) 
1920                 {
1921                         add_assoc_string(pval_res, "errorstring", 
1922                                                          (char *) ccl_err_msg(error_code), 1);
1923                         add_assoc_long(pval_res, "errorpos", error_pos);
1924                         RETVAL_FALSE;
1925                 } 
1926                 else 
1927                 {
1928                         WRBUF wrbuf_pqf = wrbuf_alloc();
1929                         ccl_stop_words_t csw = ccl_stop_words_create();
1930                         int r = ccl_stop_words_tree(csw, p->bibset, &rpn);
1931
1932                         if (r)
1933                         {
1934                                 /* stop words were removed. Return stopwords info */
1935                                 zval *zval_stopwords;
1936                                 int idx;
1937
1938                                 MAKE_STD_ZVAL(zval_stopwords);
1939                                 array_init(zval_stopwords);
1940                                 for (idx = 0; ; idx++)
1941                                 {
1942                                         zval *zval_stopword;
1943
1944                                         const char *qname;
1945                                         const char *term;
1946                                         if (!ccl_stop_words_info(csw, idx, &qname, &term))
1947                                                 break;
1948
1949                                         MAKE_STD_ZVAL(zval_stopword);
1950                                         array_init(zval_stopword);
1951
1952                                         add_assoc_string(zval_stopword, "field", (char *) qname, 1);
1953                                         add_assoc_string(zval_stopword, "term", (char *) term, 1);
1954                                         add_next_index_zval(zval_stopwords, zval_stopword);
1955                                 }
1956                                 add_assoc_zval(pval_res, "stopwords", zval_stopwords);
1957                         }
1958                         ccl_pquery(wrbuf_pqf, rpn);
1959                         add_assoc_stringl(pval_res, "rpn", 
1960                                                           wrbuf_buf(wrbuf_pqf), wrbuf_len(wrbuf_pqf), 1);
1961                         wrbuf_destroy(wrbuf_pqf);
1962                         ccl_stop_words_destroy(csw);
1963                         RETVAL_TRUE;
1964                 }
1965                 ccl_rpn_delete(rpn);
1966         } else {
1967                 RETVAL_FALSE;
1968         }
1969         release_assoc(p);
1970 }
1971 /* }}} */
1972
1973 /* {{{ proto bool yaz_database (resource id, string databases)
1974    Specify the databases within a session */
1975 PHP_FUNCTION(yaz_database)
1976 {
1977         pval *pval_id, *pval_database;
1978         Yaz_Association p;
1979
1980         if (ZEND_NUM_ARGS() != 2 ||
1981                 zend_parse_parameters(2, "zz", &pval_id, &pval_database) == FAILURE) {
1982                 WRONG_PARAM_COUNT;
1983         }
1984
1985         convert_to_string_ex(&pval_database);
1986         get_assoc(INTERNAL_FUNCTION_PARAM_PASSTHRU, pval_id, &p);
1987         option_set(p, "databaseName", pval_database->value.str.val);
1988         RETVAL_TRUE;
1989         release_assoc(p);
1990 }
1991 /* }}} */
1992
1993 /* {{{ php_yaz_init_globals
1994  */
1995 static void php_yaz_init_globals(zend_yaz_globals *yaz_globals)
1996 {
1997         yaz_globals->assoc_seq = 0;
1998         yaz_globals->max_links = 100;
1999         yaz_globals->keepalive = 120;
2000         yaz_globals->log_file = NULL;
2001         yaz_globals->log_mask = NULL;
2002 }
2003 /* }}} */
2004
2005 static void yaz_close_session(Yaz_Association *as TSRMLS_DC)
2006 {
2007         if (*as && (*as)->order == YAZSG(assoc_seq)) {
2008                 if ((*as)->persistent) {
2009                         (*as)->in_use = 0;
2010                 } else {
2011                         yaz_association_destroy(*as);
2012                         *as = 0;
2013                 }
2014         }
2015 }
2016
2017 static void yaz_close_link (zend_rsrc_list_entry *rsrc TSRMLS_DC)
2018 {
2019         Yaz_Association *as = (Yaz_Association *) rsrc->ptr;
2020         yaz_close_session(as TSRMLS_CC);
2021 }
2022
2023 /* {{{ PHP_INI_BEGIN
2024  */
2025 PHP_INI_BEGIN()
2026 #if PHP_MAJOR_VERSION >= 5
2027         STD_PHP_INI_ENTRY("yaz.max_links", "100", PHP_INI_ALL, OnUpdateLong, max_links, zend_yaz_globals, yaz_globals)
2028 #else
2029         STD_PHP_INI_ENTRY("yaz.max_links", "100", PHP_INI_ALL, OnUpdateInt, max_links, zend_yaz_globals, yaz_globals)
2030 #endif
2031 #if PHP_MAJOR_VERSION >= 5
2032         STD_PHP_INI_ENTRY("yaz.keepalive", "120", PHP_INI_ALL, OnUpdateLong, keepalive, zend_yaz_globals, yaz_globals)
2033 #else
2034         STD_PHP_INI_ENTRY("yaz.keepalive", "120", PHP_INI_ALL, OnUpdateInt, keepalive, zend_yaz_globals, yaz_globals)
2035 #endif
2036         STD_PHP_INI_ENTRY("yaz.log_file", NULL, PHP_INI_ALL, OnUpdateString, log_file, zend_yaz_globals, yaz_globals)
2037         STD_PHP_INI_ENTRY("yaz.log_mask", NULL, PHP_INI_ALL, OnUpdateString, log_mask, zend_yaz_globals, yaz_globals)
2038 PHP_INI_END()
2039 /* }}} */
2040         
2041 PHP_MINIT_FUNCTION(yaz)
2042 {
2043         int i;
2044         const char *fname;
2045         const char *mask;
2046 #ifdef ZTS
2047         yaz_mutex = tsrm_mutex_alloc();
2048 #endif
2049
2050         ZEND_INIT_MODULE_GLOBALS(yaz, php_yaz_init_globals, NULL);
2051
2052         REGISTER_INI_ENTRIES();
2053         
2054         REGISTER_LONG_CONSTANT("ZOOM_EVENT_NONE", ZOOM_EVENT_NONE,
2055                                                    CONST_CS|CONST_PERSISTENT);
2056         REGISTER_LONG_CONSTANT("ZOOM_EVENT_CONNECT", ZOOM_EVENT_CONNECT,
2057                                                    CONST_CS|CONST_PERSISTENT);
2058         REGISTER_LONG_CONSTANT("ZOOM_EVENT_SEND_DATA", ZOOM_EVENT_SEND_DATA,
2059                                                    CONST_CS|CONST_PERSISTENT);
2060         REGISTER_LONG_CONSTANT("ZOOM_EVENT_RECV_DATA", ZOOM_EVENT_RECV_DATA,
2061                                                    CONST_CS|CONST_PERSISTENT);
2062         REGISTER_LONG_CONSTANT("ZOOM_EVENT_TIMEOUT", ZOOM_EVENT_TIMEOUT,
2063                                                    CONST_CS|CONST_PERSISTENT);
2064         REGISTER_LONG_CONSTANT("ZOOM_EVENT_UNKNOWN", ZOOM_EVENT_UNKNOWN,
2065                                                    CONST_CS|CONST_PERSISTENT);
2066         REGISTER_LONG_CONSTANT("ZOOM_EVENT_SEND_APDU", ZOOM_EVENT_SEND_APDU,
2067                                                    CONST_CS|CONST_PERSISTENT);
2068         REGISTER_LONG_CONSTANT("ZOOM_EVENT_RECV_APDU", ZOOM_EVENT_RECV_APDU,
2069                                                    CONST_CS|CONST_PERSISTENT);
2070         REGISTER_LONG_CONSTANT("ZOOM_EVENT_RECV_RECORD", ZOOM_EVENT_RECV_RECORD,
2071                                                    CONST_CS|CONST_PERSISTENT);
2072         REGISTER_LONG_CONSTANT("ZOOM_EVENT_RECV_SEARCH", ZOOM_EVENT_RECV_SEARCH,
2073                                                    CONST_CS|CONST_PERSISTENT);
2074
2075         fname = YAZSG(log_file);
2076         mask = YAZSG(log_mask);
2077         if (fname && *fname)
2078         {
2079                 yaz_log_init_file(fname);
2080                 if (!mask)
2081                         mask = "all";
2082                 yaz_log_init_level(yaz_log_mask_str(mask));
2083         }
2084         else
2085                 yaz_log_init_level(0);
2086
2087         le_link = zend_register_list_destructors_ex (yaz_close_link, 0, "YAZ link", module_number);
2088
2089         order_associations = 1;
2090         shared_associations = xmalloc(sizeof(*shared_associations) * MAX_ASSOC);
2091         for (i = 0; i < MAX_ASSOC; i++) {
2092                 shared_associations[i] = 0;
2093         }
2094         return SUCCESS;
2095 }
2096
2097 PHP_MSHUTDOWN_FUNCTION(yaz)
2098 {
2099         int i;
2100
2101         if (shared_associations) {
2102                 for (i = 0; i < MAX_ASSOC; i++) {
2103                         yaz_association_destroy (shared_associations[i]);
2104                 }
2105                 xfree(shared_associations);
2106                 shared_associations = 0;
2107         }
2108 #ifdef ZTS
2109         tsrm_mutex_free (yaz_mutex);
2110 #endif
2111
2112         yaz_log_init_file(0);
2113
2114         UNREGISTER_INI_ENTRIES();
2115
2116         return SUCCESS;
2117 }
2118
2119 PHP_MINFO_FUNCTION(yaz)
2120 {
2121         char version_str[20];
2122
2123         strcpy(version_str, "unknown");
2124         yaz_version(version_str, 0);
2125         php_info_print_table_start();
2126         php_info_print_table_row(2, "YAZ Support", "enabled");
2127         php_info_print_table_row(2, "PHP/YAZ Version", PHP_YAZ_VERSION);
2128         php_info_print_table_row(2, "YAZ Version", version_str);
2129         php_info_print_table_row(2, "Compiled with YAZ version", YAZ_VERSION);
2130         php_info_print_table_end();
2131 }
2132
2133 PHP_RSHUTDOWN_FUNCTION(yaz)
2134 {
2135         long now = time(0);
2136         int i;
2137
2138 #ifdef ZTS
2139         tsrm_mutex_lock(yaz_mutex);
2140 #endif
2141         for (i = 0; i < YAZSG(max_links); i++) {
2142                 Yaz_Association *as = shared_associations + i;
2143                 if (*as)
2144                 {
2145                         if (now - (*as)->time_stamp > YAZSG(keepalive))
2146                         {
2147                                 yaz_association_destroy(*as);
2148                                 *as = 0;
2149                         }
2150                 }
2151         }
2152 #ifdef ZTS
2153         tsrm_mutex_unlock(yaz_mutex);
2154 #endif
2155         return SUCCESS;
2156 }
2157
2158 PHP_RINIT_FUNCTION(yaz)
2159 {
2160         char pidstr[20];
2161
2162         sprintf(pidstr, "%ld", (long) getpid());
2163 #ifdef ZTS
2164         tsrm_mutex_lock(yaz_mutex);
2165 #endif
2166         YAZSG(assoc_seq) = order_associations++;
2167 #ifdef ZTS
2168         tsrm_mutex_unlock(yaz_mutex);
2169 #endif
2170         yaz_log_init_prefix(pidstr);
2171         return SUCCESS;
2172 }
2173
2174 zend_module_entry yaz_module_entry = {
2175 #if ZEND_MODULE_API_NO >= 20010901
2176         STANDARD_MODULE_HEADER,
2177 #endif
2178         "yaz",
2179         yaz_functions,
2180         PHP_MINIT(yaz),
2181         PHP_MSHUTDOWN(yaz),
2182         PHP_RINIT(yaz),
2183         PHP_RSHUTDOWN(yaz),
2184         PHP_MINFO(yaz),
2185 #if ZEND_MODULE_API_NO >= 20010901
2186         PHP_YAZ_VERSION,
2187 #endif
2188         STANDARD_MODULE_PROPERTIES
2189 };
2190
2191
2192 #endif
2193
2194 /*
2195  * Local variables:
2196  * tab-width: 4
2197  * c-basic-offset: 4
2198  * End:
2199  * vim600: sw=4 ts=4 fdm=marker
2200  * vim<600: sw=4 ts=4
2201  */