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