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