1 %module xapian
2 %{
3 /* perl.i: SWIG interface file for the Perl bindings
4  *
5  * Copyright (C) 2009 Kosei Moriyama
6  * Copyright (C) 2011,2012,2013,2015,2016,2019,2020 Olly Betts
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License as
10  * published by the Free Software Foundation; either version 2 of the
11  * License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
21  * USA
22  */
23 %}
24 
25 %begin %{
26 // Older Perl headers contain things which cause warnings with more recent
27 // C++ compilers.  There's nothing we can really do about them, so just
28 // suppress them.
29 #ifdef __clang__
30 # pragma clang diagnostic push
31 # pragma clang diagnostic ignored "-Wreserved-user-defined-literal"
32 #elif defined __GNUC__
33 // Warning added in GCC 4.8 and we don't support anything older.
34 # pragma GCC diagnostic push
35 # pragma GCC diagnostic ignored "-Wliteral-suffix"
36 #endif
37 
38 extern "C" {
39 #include "EXTERN.h"
40 #include "perl.h"
41 #include "XSUB.h"
42 }
43 
44 #ifdef __clang__
45 # pragma clang diagnostic pop
46 #elif defined __GNUC__
47 # pragma GCC diagnostic pop
48 #endif
49 %}
50 
51 /* The XS Xapian never wrapped these, and they're now deprecated. */
52 #define XAPIAN_BINDINGS_SKIP_DEPRECATED_DB_FACTORIES
53 
54 %include ../xapian-head.i
55 
56 /* "next" is a keyword in Perl. */
57 %rename(increment_weight) *::next(double min_wt);
58 
59 /* Wrapping constant values. */
60 %constant int OP_AND = Xapian::Query::OP_AND;
61 %constant int OP_OR = Xapian::Query::OP_OR;
62 %constant int OP_AND_NOT = Xapian::Query::OP_AND_NOT;
63 %constant int OP_XOR = Xapian::Query::OP_XOR;
64 %constant int OP_AND_MAYBE = Xapian::Query::OP_AND_MAYBE;
65 %constant int OP_FILTER = Xapian::Query::OP_FILTER;
66 %constant int OP_NEAR = Xapian::Query::OP_NEAR;
67 %constant int OP_PHRASE = Xapian::Query::OP_PHRASE;
68 %constant int OP_VALUE_RANGE = Xapian::Query::OP_VALUE_RANGE;
69 %constant int OP_SCALE_WEIGHT = Xapian::Query::OP_SCALE_WEIGHT;
70 %constant int OP_ELITE_SET = Xapian::Query::OP_ELITE_SET;
71 %constant int OP_VALUE_GE = Xapian::Query::OP_VALUE_GE;
72 %constant int OP_SYNONYM = Xapian::Query::OP_SYNONYM;
73 %constant int OP_MAX = Xapian::Query::OP_MAX;
74 %constant int OP_WILDCARD = Xapian::Query::OP_WILDCARD;
75 %constant int OP_VALUE_LE = Xapian::Query::OP_VALUE_LE;
76 %constant int OP_INVALID = Xapian::Query::OP_INVALID;
77 %constant int ENQ_ASCENDING = Xapian::Enquire::ASCENDING;
78 %constant int ENQ_DESCENDING = Xapian::Enquire::DESCENDING;
79 %constant int ENQ_DONT_CARE = Xapian::Enquire::DONT_CARE;
80 %constant int FLAG_BOOLEAN = Xapian::QueryParser::FLAG_BOOLEAN;
81 %constant int FLAG_PHRASE = Xapian::QueryParser::FLAG_PHRASE;
82 %constant int FLAG_LOVEHATE = Xapian::QueryParser::FLAG_LOVEHATE;
83 %constant int FLAG_BOOLEAN_ANY_CASE = Xapian::QueryParser::FLAG_BOOLEAN_ANY_CASE;
84 %constant int FLAG_WILDCARD = Xapian::QueryParser::FLAG_WILDCARD;
85 %constant int FLAG_PURE_NOT = Xapian::QueryParser::FLAG_PURE_NOT;
86 %constant int FLAG_PARTIAL = Xapian::QueryParser::FLAG_PARTIAL;
87 %constant int FLAG_SPELLING_CORRECTION = Xapian::QueryParser::FLAG_SPELLING_CORRECTION;
88 %constant int FLAG_SYNONYM = Xapian::QueryParser::FLAG_SYNONYM;
89 %constant int FLAG_AUTO_SYNONYMS = Xapian::QueryParser::FLAG_AUTO_SYNONYMS;
90 %constant int FLAG_AUTO_MULTIWORD_SYNONYMS = Xapian::QueryParser::FLAG_AUTO_MULTIWORD_SYNONYMS;
91 %constant int FLAG_CJK_NGRAM = Xapian::QueryParser::FLAG_CJK_NGRAM;
92 %constant int FLAG_DEFAULT = Xapian::QueryParser::FLAG_DEFAULT;
93 %constant int STEM_NONE = Xapian::QueryParser::STEM_NONE;
94 %constant int STEM_SOME = Xapian::QueryParser::STEM_SOME;
95 %constant int STEM_SOME_FULL_POS = Xapian::QueryParser::STEM_SOME_FULL_POS;
96 %constant int STEM_ALL = Xapian::QueryParser::STEM_ALL;
97 %constant int STEM_ALL_Z = Xapian::QueryParser::STEM_ALL_Z;
98 %constant int FLAG_SPELLING = Xapian::TermGenerator::FLAG_SPELLING;
99 // FLAG_CJK_NGRAM already set above from QueryParser (values match).
100 %constant int WILDCARD_LIMIT_ERROR = Xapian::Query::WILDCARD_LIMIT_ERROR;
101 %constant int WILDCARD_LIMIT_FIRST = Xapian::Query::WILDCARD_LIMIT_FIRST;
102 %constant int WILDCARD_LIMIT_MOST_FREQUENT = Xapian::Query::WILDCARD_LIMIT_MOST_FREQUENT;
103 
104 /* Xapian::Enquire */
105 %feature("shadow") Xapian::Enquire::get_mset
106 %{
107 sub get_mset {
108   my $self = $_[0];
109   my $nargs = scalar(@_);
110   if( $nargs == 4 ) {
111     my $type = ref( $_[2] );
112     if ( $type eq 'Xapian::RSet' ) {
113       # get_mset(first, max, rset)
114       splice @_, 2, 0, (0); # insert checkatleast
115     }
116   }
117   return Xapianc::Enquire_get_mset( @_ );
118 }
119 %}
120 
121 %feature("shadow") Xapian::Enquire::set_query
122 %{
123 sub set_query {
124   if (ref($_[1]) ne 'Xapian::Query') {
125     push @_, Xapian::Query->new(splice @_, 1);
126   }
127   Xapianc::Enquire_set_query(@_);
128 }
129 %}
130 
131 %feature("shadow") Xapian::Enquire::set_sort_by_key
132 %{
133 sub set_sort_by_key {
134     my $self = $_[0];
135     my $sorter = $_[1];
136     $self{_sorter} = $sorter;
137     Xapianc::Enquire_set_sort_by_key( @_ );
138 }
139 %}
140 
141 %feature("shadow") Xapian::Enquire::set_sort_by_key_then_relevance
142 %{
143 sub set_sort_by_key_then_relevance {
144     my $self = $_[0];
145     my $sorter = $_[1];
146     $self{_sorter} = $sorter;
147     Xapianc::Enquire_set_sort_by_key_then_relevance( @_ );
148 }
149 %}
150 
151 %feature("shadow") Xapian::Enquire::set_sort_by_relevance_then_key
152 %{
153 sub set_sort_by_relevance_then_key {
154     my $self = $_[0];
155     my $sorter = $_[1];
156     $self{_sorter} = $sorter;
157     Xapianc::Enquire_set_sort_by_relevance_then_key( @_ );
158 }
159 %}
160 
161 /* Xapian::Enquire */
162 %extend Xapian::Enquire {
163 // For compatibility with Search::Xapian.
get_mset(Xapian::doccount first,Xapian::doccount maxitems,const Xapian::MatchDecider * mdecider)164 Xapian::MSet get_mset(Xapian::doccount first,
165 		      Xapian::doccount maxitems,
166 		      const Xapian::MatchDecider* mdecider) {
167     return $self->get_mset(first, maxitems, 0, NULL, mdecider);
168 }
169 }
170 
171 /* Xapian::ESet */
172 %extend Xapian::ESet {
FETCH(int index)173 Xapian::ESetIterator FETCH(int index) {
174     return ((*self)[index]);
175 }
176 }
177 
178 /* Xapian::ESetIterator */
179 %extend Xapian::ESetIterator {
get_termname()180 std::string get_termname() {
181     return self->operator*();
182 }
183 }
184 
185 /* Xapian::MSet */
186 %extend Xapian::MSet {
FETCH(int index)187 Xapian::MSetIterator FETCH(int index) {
188     return ((*self)[index]);
189 }
190 }
191 
192 /* Xapian::Query */
193 %feature("shadow") Xapian::Query::Query
194 %{
195 sub new {
196   my $class = shift;
197   my $query;
198 
199   if( @_ <= 1 ) {
200     $query = Xapianc::new_Query(@_);
201   } else {
202     use Carp;
203     my $op = $_[0];
204     if( $op !~ /^\d+$/ ) {
205 	Carp::croak( "USAGE: $class->new('term') or $class->new(OP, <args>)" );
206     }
207     if( $op == 8 ) { # FIXME: 8 is OP_VALUE_RANGE; eliminate hardcoded literal
208       if( @_ != 4 ) {
209 	Carp::croak( "USAGE: $class->new(OP_VALUE_RANGE, VALNO, START, END)" );
210       }
211       $query = Xapianc::new_Query( @_ );
212     } elsif( $op == 9 ) { # FIXME: OP_SCALE_WEIGHT
213       if( @_ != 3 ) {
214 	Carp::croak( "USAGE: $class->new(OP_SCALE_WEIGHT, QUERY, FACTOR)" );
215       }
216       $query = Xapianc::new_Query( @_ );
217     } elsif( $op == 11 || $op == 12 ) { # FIXME: OP_VALUE_GE, OP_VALUE_LE; eliminate hardcoded literals
218       if( @_ != 3 ) {
219 	Carp::croak( "USAGE: $class->new(OP_VALUE_[GL]E, VALNO, LIMIT)" );
220       }
221       $query = Xapianc::new_Query( @_ );
222     } else {
223       shift @_;
224       $query = Xapian::newN( $op, \@_ );
225     }
226   }
227   return $query;
228 }
229 %}
230 
231 %typemap(in) SV ** {
232 	AV *tempav;
233 	I32 len;
234 	int i;
235 	SV  **tv;
236 	if (!SvROK($input))
237 	    croak("Argument $argnum is not a reference.");
238 	if (SvTYPE(SvRV($input)) != SVt_PVAV)
239 	    croak("Argument $argnum is not an array.");
240 	tempav = (AV*)SvRV($input);
241 	len = av_len(tempav);
242 	$1 = (SV **) malloc((len+2)*sizeof(SV *));
243 	for (i = 0; i <= len; i++) {
244 	    tv = av_fetch(tempav, i, 0);
245 	    $1[i] = *tv;
246 	}
247 	$1[i] = NULL;
248 };
249 
250 %typemap(freearg) SV ** {
251 	free($1);
252 }
253 
254 %{
255 class XapianSWIGQueryItor {
256     AV * array;
257 
258     int i;
259 
260   public:
XapianSWIGQueryItor()261     XapianSWIGQueryItor() { }
262 
begin(AV * array_)263     void begin(AV * array_) {
264 	array = array_;
265 	i = 0;
266     }
267 
end(int n)268     void end(int n) {
269 	i = n;
270     }
271 
272     XapianSWIGQueryItor & operator++() {
273 	++i;
274 	return *this;
275     }
276 
277     Xapian::Query operator*() const {
278 	SV **svp = av_fetch(array, i, 0);
279 	if( svp == NULL )
280 	    croak("Unexpected NULL returned by av_fetch()");
281 	SV *sv = *svp;
282 
283 	if (!SvOK(sv)) {
284 	    croak("USAGE: Xapian::Query->new(OP, @TERMS_OR_QUERY_OBJECTS)");
285 	}
286 
287 	Xapian::Query *q;
288 	if (SWIG_ConvertPtr(sv, (void**)&q,
289 			    SWIGTYPE_p_Xapian__Query, 0) == SWIG_OK) {
290 	    return *q;
291 	}
292 
293 	STRLEN len;
294 	const char * ptr = SvPV(sv, len);
295 	return Xapian::Query(string(ptr, len));
296     }
297 
298     bool operator==(const XapianSWIGQueryItor & o) {
299 	return i == o.i;
300     }
301 
302     bool operator!=(const XapianSWIGQueryItor & o) {
303 	return !(*this == o);
304     }
305 
306     typedef std::input_iterator_tag iterator_category;
307     typedef Xapian::Query value_type;
308     typedef Xapian::termcount_diff difference_type;
309     typedef Xapian::Query * pointer;
310     typedef Xapian::Query & reference;
311 };
312 
313 %}
314 
315 %inline %{
newN(int op_,SV * q_)316 Xapian::Query * newN(int op_, SV *q_) {
317     Xapian::Query::op op = (Xapian::Query::op)op_;
318     XapianSWIGQueryItor b, e;
319 
320     AV *q = (AV *) SvRV(q_);
321 
322     b.begin(q);
323     e.end(av_len(q) + 1);
324 
325     try {
326 	return new Xapian::Query(op, b, e);
327     } catch (const Xapian::Error &error) {
328 	croak( "Exception: %s", error.get_msg().c_str() );
329     }
330 }
331 %}
332 
333 /* Xapian::QueryParser */
334 %feature("shadow") Xapian::QueryParser::QueryParser
335 %{
336 sub new {
337   my $class = shift;
338   my $qp = Xapianc::new_QueryParser();
339 
340   bless $qp, $class;
341   $qp->set_database(@_) if scalar(@_) == 1;
342 
343   return $qp;
344 }
345 %}
346 
347 %feature("shadow") Xapian::QueryParser::set_stopper
348 %{
349 sub set_stopper {
350     my ($self, $stopper) = @_;
351     $self{_stopper} = $stopper;
352     Xapianc::QueryParser_set_stopper( @_ );
353 }
354 %}
355 
356 %feature("shadow") Xapian::QueryParser::add_rangeprocessor
357 %{
358 sub add_rangeprocessor {
359     my ($self, $rproc) = @_;
360     push @{$self{_rproc}}, $rproc;
361     Xapianc::QueryParser_add_rangeprocessor( @_ );
362 }
363 %}
364 
365 %feature("shadow") Xapian::QueryParser::add_valuerangeprocessor
366 %{
367 sub add_valuerangeprocessor {
368     my ($self, $vrproc) = @_;
369     push @{$self{_vrproc}}, $vrproc;
370     Xapianc::QueryParser_add_valuerangeprocessor( @_ );
371 }
372 %}
373 
374 /* Xapian::SimpleStopper */
375 %feature("shadow") Xapian::SimpleStopper::SimpleStopper
376 %{
377 sub new {
378     my $class = shift;
379     my $stopper = Xapianc::new_SimpleStopper();
380 
381     bless $stopper, $class;
382     foreach (@_) {
383 	$stopper->add($_);
384     }
385 
386     return $stopper;
387 }
388 %}
389 
390 %extend Xapian::SimpleStopper {
stop_word(std::string term)391 bool stop_word(std::string term) {
392      return (*self)(term);
393 }
394 }
395 
396 /* Xapian::Stem */
397 %extend Xapian::Stem {
stem_word(std::string word)398 std::string stem_word(std::string word) {
399 	    return (*self)(word);
400 }
401 }
402 
403 /* Xapian::TermIterator */
404 %rename(get_termname) Xapian::TermIterator::get_term;
405 
406 /* Xapian::WritableDatabase */
407 %rename(replace_document_by_term) \
408 	Xapian::WritableDatabase::replace_document(const std::string &,
409 						   const Xapian::Document &);
410 %rename(delete_document_by_term) \
411 	Xapian::WritableDatabase::delete_document(const std::string &);
412 
413 %feature("shadow") Xapian::WritableDatabase::WritableDatabase
414 %{
415 sub new {
416   my $pkg = shift;
417   my $self;
418   if( scalar(@_) == 0 ) {
419     # For compatibility with Search::Xapian
420     @_ = ('', $Xapianc::DB_BACKEND_INMEMORY);
421   }
422   $self = Xapianc::new_WritableDatabase(@_);
423   bless $self, $pkg if defined($self);
424 }
425 %}
426 
427 %define SUB_CLASS(NS, CLASS)
428 %{
429 class perl##CLASS : public NS::CLASS {
430     SV* callback;
431 
432   public:
CLASS(SV * func)433     perl##CLASS(SV* func) {
434 	callback = newSVsv(func);
435     }
436 
CLASS()437     ~perl##CLASS() {
438 	SvREFCNT_dec(callback);
439     }
440 
operator()441     bool operator()(const std::string &term) const {
442 	dSP;
443 
444 	ENTER;
445 	SAVETMPS;
446 
447 	PUSHMARK(SP);
448 
449 	SV* arg = sv_newmortal();
450 	sv_setpvn(arg, term.data(), term.size());
451 	XPUSHs(arg);
452 	PUTBACK;
453 
454 	int count = call_sv(callback, G_SCALAR);
455 
456 	SPAGAIN;
457 	if (count != 1)
458 	    croak("callback function should return 1 value, got %d", count);
459 
460 	bool result = POPi;
461 
462 	PUTBACK;
463 
464 	FREETMPS;
465 	LEAVE;
466 
467 	return result;
468     }
469 };
470 %}
471 
472 %enddef
473 
474 SUB_CLASS(Xapian, ExpandDecider)
475 SUB_CLASS(Xapian, Stopper)
476 
477 %{
478 class perlMatchDecider : public Xapian::MatchDecider {
479     SV* callback;
480 
481   public:
perlMatchDecider(SV * func)482     perlMatchDecider(SV* func) {
483 	callback = newSVsv(func);
484     }
485 
~perlMatchDecider()486     ~perlMatchDecider() {
487 	SvREFCNT_dec(callback);
488     }
489 
operator()490     bool operator()(const Xapian::Document &doc) const {
491 	dSP;
492 
493 	ENTER;
494 	SAVETMPS;
495 
496 	PUSHMARK(SP);
497 
498 	XPUSHs(SWIG_NewPointerObj(const_cast<Xapian::Document*>(&doc),
499 				  SWIGTYPE_p_Xapian__Document, 0));
500 	PUTBACK;
501 
502 	int count = call_sv(callback, G_SCALAR);
503 
504 	SPAGAIN;
505 	if (count != 1)
506 	    croak("callback function should return 1 value, got %d", count);
507 
508 	bool result = POPi;
509 
510 	PUTBACK;
511 
512 	FREETMPS;
513 	LEAVE;
514 
515 	return result;
516     }
517 };
518 %}
519 
520 %{
521 class perlStemImplementation : public Xapian::StemImplementation {
522     SV* callback;
523 
524   public:
perlStemImplementation(SV * func)525     perlStemImplementation(SV* func) {
526 	callback = newSVsv(func);
527     }
528 
~perlStemImplementation()529     ~perlStemImplementation() {
530 	SvREFCNT_dec(callback);
531     }
532 
operator()533     std::string operator()(const std::string& word) {
534 	dSP;
535 
536 	ENTER;
537 	SAVETMPS;
538 
539 	PUSHMARK(SP);
540 
541 	SV* arg = sv_newmortal();
542 	sv_setpvn(arg, word.data(), word.size());
543 	XPUSHs(arg);
544 	PUTBACK;
545 
546 	int count = call_sv(callback, G_SCALAR);
547 
548 	SPAGAIN;
549 	if (count != 1)
550 	    croak("callback function should return 1 value, got %d", count);
551 
552 	SV* sv = POPs;
553 	STRLEN len;
554 	const char* ptr = SvPV(sv, len);
555 	std::string result(ptr, len);
556 
557 	PUTBACK;
558 
559 	FREETMPS;
560 	LEAVE;
561 
562 	return result;
563     }
564 
get_description()565     std::string get_description() const {
566 	return "perlStemImplementation()";
567     }
568 };
569 %}
570 
571 %{
572 class perlKeyMaker : public Xapian::KeyMaker {
573     SV* callback;
574 
575   public:
perlKeyMaker(SV * func)576     perlKeyMaker(SV* func) {
577 	callback = newSVsv(func);
578     }
579 
~perlKeyMaker()580     ~perlKeyMaker() {
581 	SvREFCNT_dec(callback);
582     }
583 
operator()584     std::string operator()(const Xapian::Document &doc) const {
585 	dSP;
586 
587 	ENTER;
588 	SAVETMPS;
589 
590 	PUSHMARK(SP);
591 
592 	XPUSHs(SWIG_NewPointerObj(const_cast<Xapian::Document*>(&doc),
593 				  SWIGTYPE_p_Xapian__Document, 0));
594 	PUTBACK;
595 
596 	int count = call_sv(callback, G_SCALAR);
597 
598 	SPAGAIN;
599 	if (count != 1)
600 	    croak("callback function should return 1 value, got %d", count);
601 
602 	SV* sv = POPs;
603 	STRLEN len;
604 	const char* ptr = SvPV(sv, len);
605 	std::string result(ptr, len);
606 
607 	PUTBACK;
608 
609 	FREETMPS;
610 	LEAVE;
611 
612 	return result;
613     }
614 };
615 %}
616 
617 %{
618 class perlRangeProcessor : public Xapian::RangeProcessor {
619     SV* callback;
620 
621   public:
perlRangeProcessor(SV * func)622     perlRangeProcessor(SV* func) {
623 	callback = newSVsv(func);
624     }
625 
~perlRangeProcessor()626     ~perlRangeProcessor() {
627 	SvREFCNT_dec(callback);
628     }
629 
operator()630     Xapian::Query operator()(const std::string& begin, const std::string& end) {
631 	dSP;
632 
633 	ENTER;
634 	SAVETMPS;
635 
636 	PUSHMARK(SP);
637 	EXTEND(SP, 2);
638 	SV* arg = sv_newmortal();
639 	sv_setpvn(arg, begin.data(), begin.size());
640 	PUSHs(arg);
641 	arg = sv_newmortal();
642 	sv_setpvn(arg, end.data(), end.size());
643 	PUSHs(arg);
644 	PUTBACK;
645 
646 	int count = call_sv(callback, G_SCALAR);
647 
648 	SPAGAIN;
649 	if (count != 1)
650 	    croak("callback function should return 1 value, got %d", count);
651 
652 	// Allow the function to return a string or Query object.
653 	SV* sv = POPs;
654 	if (!SvOK(sv))
655 	    croak("function must return a string or Query object");
656 
657 	Xapian::Query result;
658 	Xapian::Query* q;
659 	if (SWIG_ConvertPtr(sv, (void**)&q,
660 			    SWIGTYPE_p_Xapian__Query, 0) == SWIG_OK) {
661 	    result = *q;
662 	} else {
663 	    STRLEN len;
664 	    const char* ptr = SvPV(sv, len);
665 	    result = Xapian::Query(string(ptr, len));
666 	}
667 
668 	PUTBACK;
669 
670 	FREETMPS;
671 	LEAVE;
672 
673 	return result;
674     }
675 };
676 %}
677 
678 %{
679 class perlFieldProcessor : public Xapian::FieldProcessor {
680     SV* callback;
681 
682   public:
perlFieldProcessor(SV * func)683     perlFieldProcessor(SV* func) {
684 	callback = newSVsv(func);
685     }
686 
~perlFieldProcessor()687     ~perlFieldProcessor() {
688 	SvREFCNT_dec(callback);
689     }
690 
operator()691     Xapian::Query operator()(const std::string &str) {
692 	dSP;
693 
694 	ENTER;
695 	SAVETMPS;
696 
697 	PUSHMARK(SP);
698 
699 	SV* arg = sv_newmortal();
700 	sv_setpvn(arg, str.data(), str.size());
701 	XPUSHs(arg);
702 	PUTBACK;
703 
704 	int count = call_sv(callback, G_SCALAR);
705 
706 	SPAGAIN;
707 	if (count != 1)
708 	    croak("callback function should return 1 value, got %d", count);
709 
710 	// Allow the function to return a string or Query object.
711 	SV* sv = POPs;
712 	if (!SvOK(sv))
713 	    croak("function must return a string or Query object");
714 
715 	Xapian::Query result;
716 	Xapian::Query* q;
717 	if (SWIG_ConvertPtr(sv, (void**)&q,
718 			    SWIGTYPE_p_Xapian__Query, 0) == SWIG_OK) {
719 	    result = *q;
720 	} else {
721 	    STRLEN len;
722 	    const char* ptr = SvPV(sv, len);
723 	    result = Xapian::Query(string(ptr, len));
724 	}
725 
726 	PUTBACK;
727 
728 	FREETMPS;
729 	LEAVE;
730 
731 	return result;
732     }
733 };
734 %}
735 
736 %{
737 class perlMatchSpy : public Xapian::MatchSpy {
738     SV* callback;
739 
740   public:
perlMatchSpy(SV * func)741     perlMatchSpy(SV* func) {
742 	callback = newSVsv(func);
743     }
744 
~perlMatchSpy()745     ~perlMatchSpy() {
746 	SvREFCNT_dec(callback);
747     }
748 
operator()749     void operator()(const Xapian::Document &doc, double wt) {
750 	dSP;
751 
752 	ENTER;
753 	SAVETMPS;
754 
755 	PUSHMARK(SP);
756 	EXTEND(SP, 2);
757 	PUSHs(SWIG_NewPointerObj(const_cast<Xapian::Document*>(&doc),
758 				 SWIGTYPE_p_Xapian__Document, 0));
759 	mPUSHn(wt);
760 	PUTBACK;
761 
762 	(void)call_sv(callback, G_VOID);
763 
764 	SPAGAIN;
765 	PUTBACK;
766 
767 	FREETMPS;
768 	LEAVE;
769     }
770 };
771 %}
772 
773 %define SUB_CLASS_TYPEMAPS(NS, CLASS)
774 
775 %typemap(typecheck, precedence=100) NS::CLASS * {
776     SV* sv = $input;
777     void* ptr;
778     if (SWIG_ConvertPtr(sv, &ptr, $descriptor(NS::CLASS *), 0) == SWIG_OK) {
779 	(void)ptr;
780 	$1 = 1;
781     } else {
782 	/* The docs in perlapi for call_sv say:
783 	 *
784 	 *    [T]he SV may be any of a CV, a GV, a reference to a CV, a
785 	 *    reference to a GV or "SvPV(sv)" will be used as the name of the
786 	 *    sub to call.
787 	 *
788 	 * To make overloading work helpfully, we don't allow passing the name
789 	 * of a sub.  Search::Xapian did in some cases, but it seems unlikely
790 	 * anyone relied on this.
791 	 */
792 	svtype t = SvTYPE(sv);
793 	if (t == SVt_RV) {
794 	    t = SvTYPE(SvRV(sv));
795 	}
796 	$1 = (t == SVt_PVCV || t == SVt_PVGV);
797     }
798 }
799 %typemap(in) NS::CLASS * {
800     SV* sv = $input;
801     if (SWIG_ConvertPtr(sv, (void**)&$1,
802 			$descriptor(NS::CLASS *), 0) != SWIG_OK) {
803 	$1 = new perl##CLASS(sv);
804     }
805 }
806 
807 %enddef
808 SUB_CLASS_TYPEMAPS(Xapian, MatchDecider)
809 SUB_CLASS_TYPEMAPS(Xapian, ExpandDecider)
810 SUB_CLASS_TYPEMAPS(Xapian, Stopper)
811 SUB_CLASS_TYPEMAPS(Xapian, StemImplementation)
812 SUB_CLASS_TYPEMAPS(Xapian, KeyMaker)
813 SUB_CLASS_TYPEMAPS(Xapian, RangeProcessor)
814 SUB_CLASS_TYPEMAPS(Xapian, FieldProcessor)
815 SUB_CLASS_TYPEMAPS(Xapian, MatchSpy)
816 
817 %include except.i
818 %include ../xapian-headers.i
819 %include extra.i
820