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