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