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