d55930bd287e620139c6d063ffd2a956584584f8
[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                 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                 r = ZOOM_resultset_record(p->zoom_set, pos-1);
1348                 if (!strcmp(type_args[0], "string")) {
1349                         type = "render";
1350                 }
1351                 if (r) {
1352                         if (!strcmp(type_args[0], "array") ||
1353                                 !strcmp(type_args[0], "array1"))
1354                         {
1355                                 ext_grs1(return_value, type_args, r, retval_array1_grs1);
1356                         } else if (!strcmp(type_args[0], "array2")) {
1357                                 ext_grs1(return_value, type_args, r, retval_array2_grs1);
1358                         } else if (!strcmp(type_args[0], "array3")) {
1359                                 ext_grs1(return_value, type_args, r, retval_array3_grs1);
1360                         } else {
1361                                 int rlen;
1362                                 const char *info = ZOOM_record_get(r, type, &rlen);
1363                                 if (info) {
1364                                         return_value->value.str.len = (rlen > 0) ? rlen : 0;
1365                                         return_value->value.str.val =
1366                                                 estrndup(info, return_value->value.str.len);
1367                                         return_value->type = IS_STRING;
1368                                 }
1369                                 else
1370                                 {
1371                                         php_error_docref(NULL TSRMLS_CC, E_WARNING,
1372                                                                          "Bad yaz_record type %s - or unable "
1373                                                                          "to return record with type given", type);
1374                                 }
1375                         }
1376                 }
1377         }
1378         release_assoc(p);
1379 }
1380 /* }}} */
1381
1382 /* {{{ proto void yaz_syntax(resource id, string syntax)
1383    Set record syntax for retrieval */
1384 PHP_FUNCTION(yaz_syntax)
1385 {
1386         zval *pval_id;
1387         const char *syntax;
1388         int syntax_len;
1389         Yaz_Association p;
1390
1391         if (ZEND_NUM_ARGS() != 2 ||
1392                 zend_parse_parameters(2 TSRMLS_CC, "zs", &pval_id,
1393                                                           &syntax, &syntax_len) == FAILURE) {
1394                 WRONG_PARAM_COUNT;
1395         }
1396
1397         get_assoc(INTERNAL_FUNCTION_PARAM_PASSTHRU, pval_id, &p);
1398         option_set(p, "preferredRecordSyntax", syntax);
1399         release_assoc(p);
1400 }
1401 /* }}} */
1402
1403 /* {{{ proto void yaz_element(resource id, string elementsetname)
1404    Set Element-Set-Name for retrieval */
1405 PHP_FUNCTION(yaz_element)
1406 {
1407         zval *pval_id;
1408         const char *element;
1409         int element_len;
1410         Yaz_Association p;
1411
1412         if (ZEND_NUM_ARGS() != 2 ||
1413                 zend_parse_parameters(2 TSRMLS_CC, "zs", &pval_id,
1414                                                           &element, &element_len) == FAILURE) {
1415                 WRONG_PARAM_COUNT;
1416         }
1417         get_assoc(INTERNAL_FUNCTION_PARAM_PASSTHRU, pval_id, &p);
1418
1419         option_set(p, "elementSetName", element);
1420         release_assoc(p);
1421 }
1422 /* }}} */
1423
1424 /* {{{ proto void yaz_schema(resource id, string schema)
1425    Set Schema for retrieval */
1426 PHP_FUNCTION(yaz_schema)
1427 {
1428         zval *pval_id;
1429         const char *schema;
1430         int schema_len;
1431         Yaz_Association p;
1432
1433         if (ZEND_NUM_ARGS() != 2 ||
1434                 zend_parse_parameters(2 TSRMLS_CC, "zs", &pval_id,
1435                                                           &schema, &schema_len) == FAILURE) {
1436                 WRONG_PARAM_COUNT;
1437         }
1438
1439         get_assoc(INTERNAL_FUNCTION_PARAM_PASSTHRU, pval_id, &p);
1440         option_set(p, "schema", schema);
1441         release_assoc(p);
1442 }
1443 /* }}} */
1444
1445 /* {{{ proto void yaz_set_option(resource id, mixed options)
1446    Set Option(s) for connection */
1447 PHP_FUNCTION(yaz_set_option)
1448 {
1449         Yaz_Association p;
1450
1451         if (ZEND_NUM_ARGS() == 2) {
1452                 zval *pval_ar, *pval_id;
1453                 if (zend_parse_parameters(2 TSRMLS_CC, "za",
1454                                                                   &pval_id, &pval_ar) == FAILURE) {
1455                         WRONG_PARAM_COUNT;
1456                 }
1457                 get_assoc(INTERNAL_FUNCTION_PARAM_PASSTHRU, pval_id, &p);
1458                 if (p) {
1459                         HashPosition pos;
1460                         HashTable *ht;
1461                         zval **ent;
1462
1463                         ht = Z_ARRVAL_PP(&pval_ar);
1464                         for(zend_hash_internal_pointer_reset_ex(ht, &pos);
1465                                 zend_hash_get_current_data_ex(ht, (void**) &ent, &pos) == SUCCESS;
1466                                 zend_hash_move_forward_ex(ht, &pos)
1467                         ) {
1468                                 char *key;
1469                                 ulong idx;
1470 #if PHP_API_VERSION > 20010101
1471                                 int type = zend_hash_get_current_key_ex(ht, &key, 0, &idx, 0, &pos);
1472 #else
1473                                 int type = zend_hash_get_current_key_ex(ht, &key, 0, &idx, &pos);
1474 #endif
1475                                 if (type != HASH_KEY_IS_STRING || Z_TYPE_PP(ent) != IS_STRING) {
1476                                         continue;
1477                                 }
1478                                 option_set(p, key, (*ent)->value.str.val);
1479                         }
1480                         release_assoc(p);
1481                 }
1482         } else if (ZEND_NUM_ARGS() == 3) {
1483                 zval *pval_id;
1484                 char *name, *value;
1485                 int name_len, value_len;
1486                 if (zend_parse_parameters(3 TSRMLS_CC, "zss",
1487                                                                   &pval_id, &name, &name_len,
1488                                                                   &value, &value_len) == FAILURE) {
1489                         WRONG_PARAM_COUNT;
1490                 }
1491                 get_assoc (INTERNAL_FUNCTION_PARAM_PASSTHRU, pval_id, &p);
1492                 option_set(p, name, value);
1493                 release_assoc(p);
1494         } else {
1495                 WRONG_PARAM_COUNT;
1496         }
1497 }
1498 /* }}} */
1499
1500 /* {{{ proto string yaz_get_option(resource id, string name)
1501    Set Option(s) for connection */
1502 PHP_FUNCTION(yaz_get_option)
1503 {
1504         zval *pval_id;
1505         char *name;
1506         int name_len;
1507         Yaz_Association p;
1508
1509         if (ZEND_NUM_ARGS() != 2 ||
1510                 zend_parse_parameters(2 TSRMLS_CC, "zs", &pval_id, &name, &name_len)
1511                 == FAILURE) {
1512                 WRONG_PARAM_COUNT;
1513         }
1514         get_assoc(INTERNAL_FUNCTION_PARAM_PASSTHRU, pval_id, &p);
1515         if (p) {
1516                 const char *name_str, *v;
1517                 v = option_get(p, name);
1518                 if (!v) {
1519                         v = "";
1520                 }
1521                 return_value->value.str.len = strlen(v);
1522                 return_value->value.str.val = estrndup(v, return_value->value.str.len);
1523                 return_value->type = IS_STRING;
1524         } else {
1525                 RETVAL_FALSE;
1526         }
1527         release_assoc(p);
1528 }
1529 /* }}} */
1530
1531 /* {{{ proto void yaz_range(resource id, int start, int number)
1532    Set result set start point and number of records to request */
1533 PHP_FUNCTION(yaz_range)
1534 {
1535         zval *pval_id;
1536         Yaz_Association p;
1537         long start, number;
1538
1539         if (ZEND_NUM_ARGS() != 3 ||
1540                 zend_parse_parameters(3 TSRMLS_CC, "zll", &pval_id, &start, &number)
1541                 == FAILURE) {
1542                 WRONG_PARAM_COUNT;
1543         }
1544
1545         get_assoc(INTERNAL_FUNCTION_PARAM_PASSTHRU, pval_id, &p);
1546         option_set_int(p, "start", start > 0 ? start - 1 : 0);
1547         option_set_int(p, "count", number);
1548         release_assoc(p);
1549 }
1550 /* }}} */
1551
1552 /* {{{ proto void yaz_sort(resource id, string sortspec)
1553    Set result set sorting criteria */
1554 PHP_FUNCTION(yaz_sort)
1555 {
1556         zval *pval_id;
1557         const char *criteria;
1558         int criteria_len;
1559         Yaz_Association p;
1560
1561         if (ZEND_NUM_ARGS() != 2 ||
1562                 zend_parse_parameters(2 TSRMLS_CC, "zs", &pval_id, &criteria,
1563                                                           &criteria_len) == FAILURE) {
1564                 WRONG_PARAM_COUNT;
1565         }
1566
1567         get_assoc(INTERNAL_FUNCTION_PARAM_PASSTHRU, pval_id, &p);
1568         if (p) {
1569                 xfree(p->sort_criteria);
1570                 p->sort_criteria = xstrdup(criteria);
1571                 if (p->zoom_set)
1572                         ZOOM_resultset_sort(p->zoom_set, "yaz", criteria);
1573         }
1574         release_assoc(p);
1575 }
1576 /* }}} */
1577
1578 const char *ill_array_lookup(void *handle, const char *name)
1579 {
1580         return array_lookup_string((HashTable *) handle, name);
1581 }
1582
1583 /* {{{ proto void yaz_itemorder(resource id, array package)
1584    Sends Item Order request */
1585 PHP_FUNCTION(yaz_itemorder)
1586 {
1587         zval *pval_id, *pval_package;
1588         Yaz_Association p;
1589
1590         if (ZEND_NUM_ARGS() != 2 ||
1591                 zend_parse_parameters(2 TSRMLS_CC, "za", &pval_id, &pval_package) ==
1592                 FAILURE) {
1593                 WRONG_PARAM_COUNT;
1594         }
1595         get_assoc(INTERNAL_FUNCTION_PARAM_PASSTHRU, pval_id, &p);
1596         if (p) {
1597                 ZOOM_options options = ZOOM_options_create();
1598
1599                 ZOOM_options_set_callback(options,
1600                                                                   ill_array_lookup, Z_ARRVAL_PP(&pval_package));
1601                 ZOOM_package_destroy(p->zoom_package);
1602                 p->zoom_package = ZOOM_connection_package(p->zoom_conn, options);
1603                 ZOOM_package_send(p->zoom_package, "itemorder");
1604                 ZOOM_options_set_callback(options, 0, 0);
1605                 ZOOM_options_destroy(options);
1606         }
1607         release_assoc(p);
1608 }
1609 /* }}} */
1610
1611 /* {{{ proto void yaz_es(resource id, string type, array package)
1612    Sends Extended Services Request */
1613 PHP_FUNCTION(yaz_es)
1614 {
1615         zval *pval_id, *pval_package;
1616         const char *type;
1617         int type_len;
1618         Yaz_Association p;
1619
1620         if (ZEND_NUM_ARGS() != 3 ||
1621                 zend_parse_parameters(3 TSRMLS_CC, "zsa", &pval_id,
1622                                                           &type, &type_len, &pval_package) == FAILURE) {
1623                 WRONG_PARAM_COUNT;
1624         }
1625         get_assoc(INTERNAL_FUNCTION_PARAM_PASSTHRU, pval_id, &p);
1626         if (p) {
1627                 ZOOM_options options = ZOOM_options_create();
1628
1629                 ZOOM_options_set_callback(options, ill_array_lookup,
1630                                                                   Z_ARRVAL_PP(&pval_package));
1631                 ZOOM_package_destroy(p->zoom_package);
1632                 p->zoom_package = ZOOM_connection_package(p->zoom_conn, options);
1633                 ZOOM_package_send(p->zoom_package, type);
1634                 ZOOM_options_set_callback(options, 0, 0);
1635                 ZOOM_options_destroy(options);
1636         }
1637         release_assoc(p);
1638 }
1639 /* }}} */
1640
1641 /* {{{ proto void yaz_scan(resource id, type, query [, flags])
1642    Sends Scan Request */
1643 PHP_FUNCTION(yaz_scan)
1644 {
1645         zval *pval_id, *pval_flags;
1646         char *type, *query;
1647         int type_len, query_len;
1648         HashTable *flags_ht = 0;
1649         Yaz_Association p;
1650
1651         if (ZEND_NUM_ARGS() == 3) {
1652                 if (zend_parse_parameters(3 TSRMLS_CC, "zss",
1653                                                                   &pval_id, &type, &type_len,
1654                                                                   &query, &query_len) == FAILURE) {
1655                         WRONG_PARAM_COUNT;
1656                 }
1657         } else if (ZEND_NUM_ARGS() == 4) {
1658                 if (zend_parse_parameters(4 TSRMLS_CC, "zssa",
1659                                                                   &pval_id, &type, &type_len,
1660                                                                   &query, &query_len, &pval_flags) == FAILURE) {
1661                         WRONG_PARAM_COUNT;
1662                 }
1663                 flags_ht = Z_ARRVAL_PP(&pval_flags);
1664         } else {
1665                 WRONG_PARAM_COUNT;
1666         }
1667         get_assoc(INTERNAL_FUNCTION_PARAM_PASSTHRU, pval_id, &p);
1668         ZOOM_scanset_destroy(p->zoom_scan);
1669         p->zoom_scan = 0;
1670         if (p) {
1671                 option_set(p, "number", array_lookup_string(flags_ht, "number"));
1672                 option_set(p, "position", array_lookup_string(flags_ht, "position"));
1673                 option_set(p, "stepSize", array_lookup_string(flags_ht, "stepsize"));
1674                 p->zoom_scan = ZOOM_connection_scan(p->zoom_conn, query);
1675         }
1676         release_assoc(p);
1677 }
1678 /* }}} */
1679
1680 /* {{{ proto array yaz_es_result(resource id)
1681    Inspects Extended Services Result */
1682 PHP_FUNCTION(yaz_es_result)
1683 {
1684         zval *pval_id;
1685         Yaz_Association p;
1686
1687         if (ZEND_NUM_ARGS() != 1 || zend_parse_parameters(1 TSRMLS_CC, "z",
1688                                                                                                           &pval_id)     == FAILURE) {
1689                 WRONG_PARAM_COUNT;
1690         }
1691
1692         array_init(return_value);
1693
1694         get_assoc(INTERNAL_FUNCTION_PARAM_PASSTHRU, pval_id, &p);
1695         if (p && p->zoom_package) {
1696                 const char *str = ZOOM_package_option_get(p->zoom_package,
1697                                                                                                   "targetReference");
1698
1699                 if (str) {
1700                         add_assoc_string(return_value, "targetReference", (char *) str, 1);
1701                 }
1702                 str = ZOOM_package_option_get(p->zoom_package,
1703                                                                           "xmlUpdateDoc");
1704                 if (str) {
1705                         add_assoc_string(return_value, "xmlUpdateDoc", (char *) str, 1);
1706                 }
1707         }
1708         release_assoc(p);
1709 }
1710 /* }}} */
1711
1712 /* {{{ proto array yaz_scan_result(resource id [, array options])
1713    Inspects Scan Result */
1714 PHP_FUNCTION(yaz_scan_result)
1715 {
1716         zval *pval_id, *pval_opt = 0;
1717         Yaz_Association p;
1718
1719         if (ZEND_NUM_ARGS() == 2) {
1720                 if (zend_parse_parameters(2 TSRMLS_CC, "zz",
1721                                                                   &pval_id, &pval_opt) == FAILURE) {
1722                         WRONG_PARAM_COUNT;
1723                 }
1724         } else if (ZEND_NUM_ARGS() == 1) {
1725                 if (zend_parse_parameters(1 TSRMLS_CC, "z",
1726                                                                   &pval_id) == FAILURE) {
1727                         WRONG_PARAM_COUNT;
1728                 }
1729         } else {
1730                 WRONG_PARAM_COUNT;
1731         }
1732
1733         array_init(return_value);
1734
1735         if (pval_opt && array_init(pval_opt) == FAILURE) {
1736                 RETURN_FALSE;
1737         }
1738
1739         get_assoc(INTERNAL_FUNCTION_PARAM_PASSTHRU, pval_id, &p);
1740         if (p && p->zoom_scan) {
1741                 int pos = 0;
1742                 /* ZOOM_scanset_term changed from YAZ 3 to YAZ 4 */
1743 #if YAZ_VERSIONL >= 0x040000
1744                 size_t occ, len;
1745 #else
1746                 int occ, len;
1747 #endif
1748                 int size = ZOOM_scanset_size(p->zoom_scan);
1749
1750                 for (pos = 0; pos < size; pos++) {
1751                         const char *term = ZOOM_scanset_term(p->zoom_scan, pos, &occ, &len);
1752                         zval *my_zval;
1753
1754                         ALLOC_ZVAL(my_zval);
1755                         array_init(my_zval);
1756                         INIT_PZVAL(my_zval);
1757
1758                         add_next_index_string(my_zval, "term", 1);
1759
1760                         if (term) {
1761                                 add_next_index_stringl(my_zval, (char*) term, len, 1);
1762                         } else {
1763                                 add_next_index_string(my_zval, "?", 1);
1764                         }
1765                         add_next_index_long(my_zval, occ);
1766
1767                         term = ZOOM_scanset_display_term(p->zoom_scan, pos, &occ, &len);
1768
1769                         if (term) {
1770                                 add_next_index_stringl(my_zval, (char*) term, len, 1);
1771                         } else {
1772                                 add_next_index_string(my_zval, "?", 1);
1773                         }
1774
1775                         zend_hash_next_index_insert(return_value->value.ht, (void *) &my_zval, sizeof(zval *), NULL);
1776                 }
1777
1778                 if (pval_opt) {
1779                         const char *v;
1780
1781                         add_assoc_long(pval_opt, "number", size);
1782
1783                         v = ZOOM_scanset_option_get(p->zoom_scan, "stepSize");
1784                         if (v) {
1785                                 add_assoc_long(pval_opt, "stepsize", atoi(v));
1786                         }
1787                         v = ZOOM_scanset_option_get(p->zoom_scan, "position");
1788                         if (v) {
1789                                 add_assoc_long(pval_opt, "position", atoi(v));
1790                         }
1791                         v = ZOOM_scanset_option_get(p->zoom_scan, "scanStatus");
1792                         if (v) {
1793                                 add_assoc_long(pval_opt, "status", atoi(v));
1794                         }
1795                 }
1796         }
1797         release_assoc(p);
1798 }
1799 /* }}} */
1800
1801 /* {{{ proto void yaz_ccl_conf(resource id, array package)
1802    Configure CCL package */
1803 PHP_FUNCTION(yaz_ccl_conf)
1804 {
1805         zval *pval_id, *pval_package;
1806         Yaz_Association p;
1807
1808         if (ZEND_NUM_ARGS() != 2 ||
1809                 zend_parse_parameters(2 TSRMLS_CC, "za", &pval_id, &pval_package)
1810                 == FAILURE) {
1811                 WRONG_PARAM_COUNT;
1812         }
1813         get_assoc(INTERNAL_FUNCTION_PARAM_PASSTHRU, pval_id, &p);
1814         if (p) {
1815                 HashTable *ht = Z_ARRVAL_PP(&pval_package);
1816                 HashPosition pos;
1817                 zval **ent;
1818                 char *key;
1819
1820                 ccl_qual_rm(&p->bibset);
1821                 p->bibset = ccl_qual_mk();
1822
1823                 for (zend_hash_internal_pointer_reset_ex(ht, &pos);
1824                         zend_hash_get_current_data_ex(ht, (void**) &ent, &pos) == SUCCESS;
1825                         zend_hash_move_forward_ex(ht, &pos)
1826                 ) {
1827                         ulong idx;
1828 #if PHP_API_VERSION > 20010101
1829                         int type = zend_hash_get_current_key_ex(ht, &key, 0, &idx, 0, &pos);
1830 #else
1831                         int type = zend_hash_get_current_key_ex(ht, &key, 0, &idx, &pos);
1832 #endif
1833                         if (type != HASH_KEY_IS_STRING || Z_TYPE_PP(ent) != IS_STRING) {
1834                                 continue;
1835                         }
1836                         ccl_qual_fitem(p->bibset, (*ent)->value.str.val, key);
1837                 }
1838         }
1839         release_assoc(p);
1840 }
1841 /* }}} */
1842
1843 /* {{{ proto bool yaz_ccl_parse(resource id, string query, array res)
1844    Parse a CCL query */
1845 PHP_FUNCTION(yaz_ccl_parse)
1846 {
1847         zval *pval_id, *pval_res = 0;
1848         char *query;
1849         int query_len;
1850         Yaz_Association p;
1851
1852         if (ZEND_NUM_ARGS() != 3 ||
1853                 zend_parse_parameters(3 TSRMLS_CC, "zsz",
1854                                                           &pval_id, &query, &query_len, &pval_res)
1855                 == FAILURE) {
1856                 WRONG_PARAM_COUNT;
1857         }
1858
1859         zval_dtor(pval_res);
1860         array_init(pval_res);
1861         get_assoc(INTERNAL_FUNCTION_PARAM_PASSTHRU, pval_id, &p);
1862         if (p) {
1863                 struct ccl_rpn_node *rpn;
1864                 int error_pos;
1865                 int error_code;
1866                 CCL_parser ccl_parser = ccl_parser_create(p->bibset);
1867
1868                 rpn = ccl_parser_find_str(ccl_parser, query);
1869
1870                 error_code = ccl_parser_get_error(ccl_parser, &error_pos);
1871                 add_assoc_long(pval_res, "errorcode", error_code);
1872
1873                 if (error_code)
1874                 {
1875                         add_assoc_string(pval_res, "errorstring",
1876                                                          (char *) ccl_err_msg(error_code), 1);
1877                         add_assoc_long(pval_res, "errorpos", error_pos);
1878                         RETVAL_FALSE;
1879                 }
1880                 else
1881                 {
1882                         WRBUF wrbuf_pqf = wrbuf_alloc();
1883                         ccl_stop_words_t csw = ccl_stop_words_create();
1884                         int r = ccl_stop_words_tree(csw, p->bibset, &rpn);
1885
1886                         if (r)
1887                         {
1888                                 /* stop words were removed. Return stopwords info */
1889                                 zval *zval_stopwords;
1890                                 int idx;
1891
1892                                 MAKE_STD_ZVAL(zval_stopwords);
1893                                 array_init(zval_stopwords);
1894                                 for (idx = 0; ; idx++)
1895                                 {
1896                                         zval *zval_stopword;
1897
1898                                         const char *qname;
1899                                         const char *term;
1900                                         if (!ccl_stop_words_info(csw, idx, &qname, &term))
1901                                                 break;
1902
1903                                         MAKE_STD_ZVAL(zval_stopword);
1904                                         array_init(zval_stopword);
1905
1906                                         add_assoc_string(zval_stopword, "field", (char *) qname, 1);
1907                                         add_assoc_string(zval_stopword, "term", (char *) term, 1);
1908                                         add_next_index_zval(zval_stopwords, zval_stopword);
1909                                 }
1910                                 add_assoc_zval(pval_res, "stopwords", zval_stopwords);
1911                         }
1912                         ccl_pquery(wrbuf_pqf, rpn);
1913                         add_assoc_stringl(pval_res, "rpn",
1914                                                           wrbuf_buf(wrbuf_pqf), wrbuf_len(wrbuf_pqf), 1);
1915                         wrbuf_destroy(wrbuf_pqf);
1916                         ccl_stop_words_destroy(csw);
1917                         RETVAL_TRUE;
1918                 }
1919                 ccl_rpn_delete(rpn);
1920         } else {
1921                 RETVAL_FALSE;
1922         }
1923         release_assoc(p);
1924 }
1925 /* }}} */
1926
1927 /* {{{ proto bool yaz_database (resource id, string databases)
1928    Specify the databases within a session */
1929 PHP_FUNCTION(yaz_database)
1930 {
1931         zval *pval_id;
1932         char *database;
1933         int database_len;
1934         Yaz_Association p;
1935
1936         if (ZEND_NUM_ARGS() != 2 ||
1937                 zend_parse_parameters(2 TSRMLS_CC, "zs", &pval_id,
1938                                                           &database, &database_len) == FAILURE) {
1939                 WRONG_PARAM_COUNT;
1940         }
1941
1942         get_assoc(INTERNAL_FUNCTION_PARAM_PASSTHRU, pval_id, &p);
1943         option_set(p, "databaseName", database);
1944         RETVAL_TRUE;
1945         release_assoc(p);
1946 }
1947 /* }}} */
1948
1949 /* {{{ php_yaz_init_globals
1950  */
1951 static void php_yaz_init_globals(zend_yaz_globals *yaz_globals)
1952 {
1953         yaz_globals->assoc_seq = 0;
1954         yaz_globals->max_links = 100;
1955         yaz_globals->keepalive = 120;
1956         yaz_globals->log_file = NULL;
1957         yaz_globals->log_mask = NULL;
1958 }
1959 /* }}} */
1960
1961 static void yaz_close_session(Yaz_Association *as TSRMLS_DC)
1962 {
1963         if (*as && (*as)->order == YAZSG(assoc_seq)) {
1964                 if ((*as)->persistent) {
1965                         (*as)->in_use = 0;
1966                 } else {
1967                         yaz_association_destroy(*as);
1968                         *as = 0;
1969                 }
1970         }
1971 }
1972
1973 static void yaz_close_link(zend_rsrc_list_entry *rsrc TSRMLS_DC)
1974 {
1975         Yaz_Association *as = (Yaz_Association *) rsrc->ptr;
1976         yaz_close_session(as TSRMLS_CC);
1977 }
1978
1979 /* {{{ PHP_INI_BEGIN
1980  */
1981 PHP_INI_BEGIN()
1982 #if PHP_MAJOR_VERSION >= 5
1983         STD_PHP_INI_ENTRY("yaz.max_links", "100", PHP_INI_ALL, OnUpdateLong, max_links, zend_yaz_globals, yaz_globals)
1984 #else
1985         STD_PHP_INI_ENTRY("yaz.max_links", "100", PHP_INI_ALL, OnUpdateInt, max_links, zend_yaz_globals, yaz_globals)
1986 #endif
1987 #if PHP_MAJOR_VERSION >= 5
1988         STD_PHP_INI_ENTRY("yaz.keepalive", "120", PHP_INI_ALL, OnUpdateLong, keepalive, zend_yaz_globals, yaz_globals)
1989 #else
1990         STD_PHP_INI_ENTRY("yaz.keepalive", "120", PHP_INI_ALL, OnUpdateInt, keepalive, zend_yaz_globals, yaz_globals)
1991 #endif
1992         STD_PHP_INI_ENTRY("yaz.log_file", NULL, PHP_INI_ALL, OnUpdateString, log_file, zend_yaz_globals, yaz_globals)
1993         STD_PHP_INI_ENTRY("yaz.log_mask", NULL, PHP_INI_ALL, OnUpdateString, log_mask, zend_yaz_globals, yaz_globals)
1994 PHP_INI_END()
1995 /* }}} */
1996
1997 PHP_MINIT_FUNCTION(yaz)
1998 {
1999         int i;
2000         const char *fname;
2001         const char *mask;
2002 #ifdef ZTS
2003         yaz_mutex = tsrm_mutex_alloc();
2004 #endif
2005
2006         ZEND_INIT_MODULE_GLOBALS(yaz, php_yaz_init_globals, NULL);
2007
2008         REGISTER_INI_ENTRIES();
2009
2010         REGISTER_LONG_CONSTANT("ZOOM_EVENT_NONE", ZOOM_EVENT_NONE,
2011                                                    CONST_CS|CONST_PERSISTENT);
2012         REGISTER_LONG_CONSTANT("ZOOM_EVENT_CONNECT", ZOOM_EVENT_CONNECT,
2013                                                    CONST_CS|CONST_PERSISTENT);
2014         REGISTER_LONG_CONSTANT("ZOOM_EVENT_SEND_DATA", ZOOM_EVENT_SEND_DATA,
2015                                                    CONST_CS|CONST_PERSISTENT);
2016         REGISTER_LONG_CONSTANT("ZOOM_EVENT_RECV_DATA", ZOOM_EVENT_RECV_DATA,
2017                                                    CONST_CS|CONST_PERSISTENT);
2018         REGISTER_LONG_CONSTANT("ZOOM_EVENT_TIMEOUT", ZOOM_EVENT_TIMEOUT,
2019                                                    CONST_CS|CONST_PERSISTENT);
2020         REGISTER_LONG_CONSTANT("ZOOM_EVENT_UNKNOWN", ZOOM_EVENT_UNKNOWN,
2021                                                    CONST_CS|CONST_PERSISTENT);
2022         REGISTER_LONG_CONSTANT("ZOOM_EVENT_SEND_APDU", ZOOM_EVENT_SEND_APDU,
2023                                                    CONST_CS|CONST_PERSISTENT);
2024         REGISTER_LONG_CONSTANT("ZOOM_EVENT_RECV_APDU", ZOOM_EVENT_RECV_APDU,
2025                                                    CONST_CS|CONST_PERSISTENT);
2026         REGISTER_LONG_CONSTANT("ZOOM_EVENT_RECV_RECORD", ZOOM_EVENT_RECV_RECORD,
2027                                                    CONST_CS|CONST_PERSISTENT);
2028         REGISTER_LONG_CONSTANT("ZOOM_EVENT_RECV_SEARCH", ZOOM_EVENT_RECV_SEARCH,
2029                                                    CONST_CS|CONST_PERSISTENT);
2030
2031         fname = YAZSG(log_file);
2032         mask = YAZSG(log_mask);
2033         if (fname && *fname)
2034         {
2035                 yaz_log_init_file(fname);
2036                 if (!mask)
2037                         mask = "all";
2038                 yaz_log_init_level(yaz_log_mask_str(mask));
2039         }
2040         else
2041                 yaz_log_init_level(0);
2042
2043         le_link = zend_register_list_destructors_ex(yaz_close_link, 0, "YAZ link", module_number);
2044
2045         order_associations = 1;
2046         shared_associations = xmalloc(sizeof(*shared_associations) * MAX_ASSOC);
2047         for (i = 0; i < MAX_ASSOC; i++) {
2048                 shared_associations[i] = 0;
2049         }
2050         return SUCCESS;
2051 }
2052
2053 PHP_MSHUTDOWN_FUNCTION(yaz)
2054 {
2055         int i;
2056
2057         if (shared_associations) {
2058                 for (i = 0; i < MAX_ASSOC; i++) {
2059                         yaz_association_destroy (shared_associations[i]);
2060                 }
2061                 xfree(shared_associations);
2062                 shared_associations = 0;
2063         }
2064 #ifdef ZTS
2065         tsrm_mutex_free(yaz_mutex);
2066 #endif
2067
2068         yaz_log_init_file(0);
2069
2070         UNREGISTER_INI_ENTRIES();
2071
2072         return SUCCESS;
2073 }
2074
2075 PHP_MINFO_FUNCTION(yaz)
2076 {
2077         char version_str[20];
2078
2079         strcpy(version_str, "unknown");
2080         yaz_version(version_str, 0);
2081         php_info_print_table_start();
2082         php_info_print_table_row(2, "YAZ Support", "enabled");
2083         php_info_print_table_row(2, "PHP/YAZ Version", PHP_YAZ_VERSION);
2084         php_info_print_table_row(2, "YAZ Version", version_str);
2085         php_info_print_table_row(2, "Compiled with YAZ version", YAZ_VERSION);
2086         php_info_print_table_end();
2087 }
2088
2089 PHP_RSHUTDOWN_FUNCTION(yaz)
2090 {
2091         long now = time(0);
2092         int i;
2093
2094 #ifdef ZTS
2095         tsrm_mutex_lock(yaz_mutex);
2096 #endif
2097         for (i = 0; i < YAZSG(max_links); i++) {
2098                 Yaz_Association *as = shared_associations + i;
2099                 if (*as)
2100                 {
2101                         if (now - (*as)->time_stamp > YAZSG(keepalive))
2102                         {
2103                                 yaz_association_destroy(*as);
2104                                 *as = 0;
2105                         }
2106                 }
2107         }
2108 #ifdef ZTS
2109         tsrm_mutex_unlock(yaz_mutex);
2110 #endif
2111         return SUCCESS;
2112 }
2113
2114 PHP_RINIT_FUNCTION(yaz)
2115 {
2116         char pidstr[20];
2117
2118         sprintf(pidstr, "%ld", (long) getpid());
2119 #ifdef ZTS
2120         tsrm_mutex_lock(yaz_mutex);
2121 #endif
2122         YAZSG(assoc_seq) = order_associations++;
2123 #ifdef ZTS
2124         tsrm_mutex_unlock(yaz_mutex);
2125 #endif
2126         yaz_log_init_prefix(pidstr);
2127         return SUCCESS;
2128 }
2129
2130 zend_module_entry yaz_module_entry = {
2131 #if ZEND_MODULE_API_NO >= 20010901
2132         STANDARD_MODULE_HEADER,
2133 #endif
2134         "yaz",
2135         yaz_functions,
2136         PHP_MINIT(yaz),
2137         PHP_MSHUTDOWN(yaz),
2138         PHP_RINIT(yaz),
2139         PHP_RSHUTDOWN(yaz),
2140         PHP_MINFO(yaz),
2141 #if ZEND_MODULE_API_NO >= 20010901
2142         PHP_YAZ_VERSION,
2143 #endif
2144         STANDARD_MODULE_PROPERTIES
2145 };
2146
2147
2148 #endif
2149
2150 /*
2151  * Local variables:
2152  * tab-width: 4
2153  * c-basic-offset: 4
2154  * End:
2155  * vim600: sw=4 ts=4 fdm=marker
2156  * vim<600: sw=4 ts=4
2157  */