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