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