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