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