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