1 //
2 // $Id$
3 //
4 
5 //
6 // Copyright (c) 2001-2016, Andrew Aksyonoff
7 // Copyright (c) 2008-2016, Sphinx Technologies Inc
8 // All rights reserved
9 //
10 // This program is free software; you can redistribute it and/or modify
11 // it under the terms of the GNU Library General Public License. You should
12 // have received a copy of the LGPL license along with this program; if you
13 // did not, you can find it at http://www.gnu.org/
14 //
15 
16 // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
17 //							WARNING
18 // We strongly recommend you to use SphinxQL instead of an API
19 // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
20 
21 #ifdef _WIN32
22 #if _MSC_VER>=1400
23 // VS 2005 and above
24 #define _CRT_SECURE_NO_DEPRECATE 1
25 #define _CRT_NONSTDC_NO_DEPRECATE 1
26 #else
27 // VS 2003 and below
28 #define vsnprintf _vsnprintf
29 #endif
30 #endif
31 
32 #include <stdlib.h>
33 #include <stdarg.h>
34 #include <stdio.h>
35 #include <string.h>
36 
37 #ifndef _WIN32
38 #include "sphinxclient_config.h"
39 #endif
40 
41 #include "sphinxclient.h"
42 
43 #if _WIN32
44 	// Win-specific headers, calls, libraries
45 	#include <io.h>
46 	#include <winsock2.h>
47 
48 	#pragma comment(linker, "/defaultlib:wsock32.lib")
49 	#pragma message("Automatically linking with wsock32.lib")
50 
51 	#define EWOULDBLOCK			WSAEWOULDBLOCK
52 	#define EINTR				WSAEINTR
53 
54 #else
55 	// UNIX-specific headers and calls
56 	#include <unistd.h>
57 	#include <netinet/in.h>
58 	#include <sys/file.h>
59 	#include <sys/socket.h>
60 	#include <sys/time.h>
61 	#include <sys/wait.h>
62 	#include <netdb.h>
63 	#include <errno.h>
64 	#include <sys/un.h>
65 	#include <sys/fcntl.h>
66 #endif
67 
68 //////////////////////////////////////////////////////////////////////////
69 
70 #define MAX_REQS				32
71 #define CONNECT_TIMEOUT_MSEC	1000
72 #define MAX_PACKET_LEN			(8*1024*1024)
73 
74 enum
75 {
76 	SEARCHD_COMMAND_SEARCH		= 0,
77 	SEARCHD_COMMAND_EXCERPT		= 1,
78 	SEARCHD_COMMAND_UPDATE		= 2,
79 	SEARCHD_COMMAND_KEYWORDS	= 3,
80 	SEARCHD_COMMAND_PERSIST		= 4,
81 	SEARCHD_COMMAND_STATUS		= 5
82 };
83 
84 enum
85 {
86 	VER_COMMAND_EXCERPT			= 0x103,
87 	VER_COMMAND_UPDATE			= 0x102,
88 	VER_COMMAND_KEYWORDS		= 0x100,
89 	VER_COMMAND_STATUS			= 0x101
90 };
91 
92 //////////////////////////////////////////////////////////////////////////
93 
94 struct st_filter
95 {
96 	const char *			attr;
97 	int						filter_type;
98 	int						num_values;
99 	const sphinx_int64_t *	values;
100 	sphinx_int64_t			umin;
101 	sphinx_int64_t			umax;
102 	float					fmin;
103 	float					fmax;
104 	int						exclude;
105 	const char *			svalue;
106 };
107 
108 
109 union un_attr_value
110 {
111 	sphinx_int64_t			int_value;
112 	float					float_value;
113 	unsigned int *			mva_value;
114 	const char *			string;
115 };
116 
117 
118 struct st_override
119 {
120 	const char *			attr;
121 	const sphinx_uint64_t *	docids;
122 	int						num_values;
123 	const unsigned int *	uint_values;
124 };
125 
126 
127 struct st_sphinx_client
128 {
129 	unsigned short			ver_search;				///< compatibility mode
130 	sphinx_bool				copy_args;				///< whether to create a copy of each passed argument
131 	void *					head_alloc;				///< head of client-owned allocations list
132 
133 	const char *			error;					///< last error
134 	const char *			warning;				///< last warning
135 	char					local_error_buf[256];	///< buffer to store 'local' error messages (eg. connect() error)
136 
137 	const char *			host;
138 	int						port;
139 	float					timeout;
140 	int						offset;
141 	int						limit;
142 	int						mode;
143 	int						num_weights;
144 	const int *				weights;
145 	int						sort;
146 	const char *			sortby;
147 	sphinx_uint64_t			minid;
148 	sphinx_uint64_t			maxid;
149 	const char *			group_by;
150 	int						group_func;
151 	const char *			group_sort;
152 	const char *			group_distinct;
153 	int						max_matches;
154 	int						cutoff;
155 	int						retry_count;
156 	int						retry_delay;
157 	const char *			geoanchor_attr_lat;
158 	const char *			geoanchor_attr_long;
159 	float					geoanchor_lat;
160 	float					geoanchor_long;
161 	int						num_filters;
162 	int						max_filters;
163 	struct st_filter *		filters;
164 	int						num_index_weights;
165 	const char **			index_weights_names;
166 	const int *				index_weights_values;
167 	int						ranker;
168 	const char *					rankexpr;
169 	int						max_query_time;
170 	int						num_field_weights;
171 	const char **			field_weights_names;
172 	const int *				field_weights_values;
173 	int						num_overrides;
174 	int						max_overrides;
175 	struct st_override *	overrides;
176 	const char *			select_list;
177 	int						query_flags;
178 	int						predicted_time;
179 	const char *			outer_orderby;
180 	int						outer_offset;
181 	int						outer_limit;
182 	sphinx_bool				has_outer;
183 
184 
185 	int						num_reqs;
186 	int						req_lens [ MAX_REQS ];
187 	char *					reqs [ MAX_REQS ];
188 
189 	int						response_len;
190 	char *					response_buf;	///< where the buffer begins (might also contain heading warning)
191 	char *					response_start;	///< where the data to parse starts
192 
193 	int						num_results;
194 	sphinx_result			results [ MAX_REQS ];
195 
196 	int						sock;			///< open socket for pconns; -1 if none
197 	sphinx_bool				persist;
198 };
199 
200 //////////////////////////////////////////////////////////////////////////
201 
202 static void *				chain ( sphinx_client * client, const void * ptr, size_t len );
203 static const char *			strchain ( sphinx_client * client, const char * s );
204 static void					unchain ( sphinx_client * client, const void * ptr );
205 static void					unchain_all ( sphinx_client * client );
206 
sphinx_create(sphinx_bool copy_args)207 sphinx_client * sphinx_create ( sphinx_bool copy_args )
208 {
209 	sphinx_client * client;
210 	int i;
211 
212 	// allocate
213 	client = malloc ( sizeof(sphinx_client) );
214 	if ( !client )
215 		return NULL;
216 
217 	// initialize defaults and return
218 	client->ver_search				= 0x11E; // 0x113 for 0.9.8, 0x116 for 0.9.9rc2
219 	client->copy_args				= copy_args;
220 	client->head_alloc				= NULL;
221 
222 	client->error					= NULL;
223 	client->warning					= NULL;
224 	client->local_error_buf[0]		= '\0';
225 
226 	client->host					= strchain ( client, "localhost" );
227 	client->port					= 9312;
228 	client->timeout					= 0.0f;
229 	client->offset					= 0;
230 	client->limit					= 20;
231 	client->mode					= SPH_MATCH_EXTENDED2;
232 	client->num_weights				= 0;
233 	client->weights					= NULL;
234 	client->sort					= SPH_SORT_RELEVANCE;
235 	client->sortby					= NULL;
236 	client->minid					= 0;
237 	client->maxid					= 0;
238 	client->group_by				= NULL;
239 	client->group_func				= SPH_GROUPBY_ATTR;
240 	client->group_sort				= strchain ( client, "@groupby desc" );
241 	client->group_distinct			= NULL;
242 	client->max_matches				= 1000;
243 	client->cutoff					= 0;
244 	client->retry_count				= 0;
245 	client->retry_delay				= 0;
246 	client->geoanchor_attr_lat		= NULL;
247 	client->geoanchor_attr_long		= NULL;
248 	client->geoanchor_lat			= 0.0f;
249 	client->geoanchor_long			= 0.0f;
250 	client->num_filters				= 0;
251 	client->max_filters				= 0;
252 	client->filters					= NULL;
253 	client->num_index_weights		= 0;
254 	client->index_weights_names		= NULL;
255 	client->index_weights_values	= NULL;
256 	client->ranker					= SPH_RANK_DEFAULT;
257 	client->rankexpr				= NULL;
258 	client->max_query_time			= 0;
259 	client->num_field_weights		= 0;
260 	client->field_weights_names		= NULL;
261 	client->field_weights_values	= NULL;
262 	client->num_overrides			= 0;
263 	client->max_overrides			= 0;
264 	client->overrides				= NULL;
265 	client->select_list				= NULL;
266 	client->query_flags				= 1<<6;
267 	client->predicted_time			= 0;
268 	client->outer_orderby			= NULL;
269 	client->outer_offset			= 0;
270 	client->outer_limit				= 0;
271 	client->has_outer				= SPH_FALSE;
272 
273 	client->num_reqs				= 0;
274 	client->response_len			= 0;
275 	client->response_buf			= NULL;
276 	client->num_results				= 0;
277 
278 	for ( i=0; i<MAX_REQS; i++ )
279 	{
280 		client->results[i].values_pool = NULL;
281 		client->results[i].words = NULL;
282 		client->results[i].fields = NULL;
283 		client->results[i].attr_names = NULL;
284 		client->results[i].attr_types = NULL;
285 	}
286 
287 	client->sock = -1;
288 	client->persist = SPH_FALSE;
289 	return client;
290 }
291 
292 
sphinx_free_results(sphinx_client * client)293 static void sphinx_free_results ( sphinx_client * client )
294 {
295 	int i;
296 	for ( i=0; i<client->num_results; i++ )
297 	{
298 		free ( client->results[i].values_pool );
299 		free ( client->results[i].words );
300 		free ( client->results[i].fields );
301 		free ( client->results[i].attr_names );
302 		free ( client->results[i].attr_types );
303 
304 		client->results[i].values_pool = NULL;
305 		client->results[i].words = NULL;
306 		client->results[i].fields = NULL;
307 		client->results[i].attr_names = NULL;
308 		client->results[i].attr_types = NULL;
309 	}
310 	client->num_results = 0;
311 }
312 
313 
314 void sock_close ( int sock );
315 
316 
317 #define safe_free(_ptr) \
318 	if ( _ptr ) \
319 	{ \
320 		free ( _ptr ); \
321 		_ptr = NULL; \
322 }
323 
324 
sphinx_cleanup(sphinx_client * client)325 void sphinx_cleanup ( sphinx_client * client )
326 {
327 	int i;
328 	if ( !client )
329 		return;
330 
331 	for ( i=0; i<client->num_reqs; i++ )
332 		safe_free ( client->reqs[i] );
333 	client->num_reqs = 0;
334 
335 	sphinx_free_results ( client );
336 	client->num_results = 0;
337 
338 	safe_free ( client->response_buf );
339 }
340 
341 
sphinx_destroy(sphinx_client * client)342 void sphinx_destroy ( sphinx_client * client )
343 {
344 	int i;
345 	if ( !client )
346 		return;
347 
348 	for ( i=0; i<client->num_reqs; i++ )
349 		safe_free ( client->reqs[i] );
350 
351 	sphinx_free_results ( client );
352 	unchain_all ( client );
353 	safe_free ( client->filters );
354 	safe_free ( client->response_buf );
355 
356 	if ( client->sock>=0 )
357 		sock_close ( client->sock );
358 
359 	free ( client );
360 }
361 
362 
sphinx_error(sphinx_client * client)363 const char * sphinx_error ( sphinx_client * client )
364 {
365 	return client->error ? client->error : "";
366 }
367 
368 
sphinx_warning(sphinx_client * client)369 const char * sphinx_warning ( sphinx_client * client )
370 {
371 	return client->warning ? client->warning : "";
372 }
373 
374 
set_error(sphinx_client * client,const char * template,...)375 static void set_error ( sphinx_client * client, const char * template, ... )
376 {
377 	va_list ap;
378 
379 	if ( !client )
380 		return;
381 
382 	va_start ( ap, template );
383 	vsnprintf ( client->local_error_buf, sizeof(client->local_error_buf), template, ap );
384 	va_end ( ap );
385 
386 	client->error = client->local_error_buf;
387 	client->warning = NULL;
388 }
389 
390 
391 //////////////////////////////////////////////////////////////////////////
392 
393 struct st_memblock
394 {
395 	struct st_memblock *	prev;
396 	struct st_memblock *	next;
397 };
398 
399 
chain(sphinx_client * client,const void * ptr,size_t len)400 static void * chain ( sphinx_client * client, const void * ptr, size_t len )
401 {
402 	struct st_memblock * entry;
403 
404 	if ( !client->copy_args || !ptr )
405 		return (void*) ptr;
406 
407 	entry = malloc ( sizeof(struct st_memblock) + len );
408 	if ( !entry )
409 	{
410 		set_error ( client, "malloc() failed (bytes=%d)", sizeof(struct st_memblock) + len );
411 		return NULL;
412 	}
413 
414 	entry->prev = NULL;
415 	entry->next = client->head_alloc;
416 	if ( entry->next )
417 		entry->next->prev = entry;
418 	client->head_alloc = entry;
419 
420 	entry++;
421 	memcpy ( entry, ptr, len );
422 	return entry;
423 }
424 
425 
strchain(sphinx_client * client,const char * s)426 static const char * strchain ( sphinx_client * client, const char * s )
427 {
428 	return s ? chain ( client, s, 1+strlen(s) ) : NULL;
429 }
430 
431 
unchain(sphinx_client * client,const void * ptr)432 static void unchain ( sphinx_client * client, const void * ptr )
433 {
434 	struct st_memblock * entry;
435 
436 	if ( !client->copy_args || !ptr )
437 		return;
438 
439 	entry = (struct st_memblock*) ptr;
440 	entry--;
441 
442 	if ( entry->prev )
443 		entry->prev->next = entry->next;
444 	else
445 		client->head_alloc = entry->next;
446 
447 	if ( entry->next )
448 		entry->next->prev = entry->prev;
449 
450 	free ( entry );
451 }
452 
453 
unchain_all(sphinx_client * client)454 static void unchain_all ( sphinx_client * client )
455 {
456 	struct st_memblock *to_free, *cur;
457 
458 	if ( !client || !client->copy_args )
459 		return;
460 
461 	cur = client->head_alloc;
462 	while ( cur )
463 	{
464 		to_free = cur;
465 		cur = cur->next;
466 		free ( to_free );
467 	}
468 	client->head_alloc = NULL;
469 }
470 
471 //////////////////////////////////////////////////////////////////////////
472 
sphinx_set_server(sphinx_client * client,const char * host,int port)473 sphinx_bool sphinx_set_server ( sphinx_client * client, const char * host, int port )
474 {
475 	if ( !client || !host || !host[0] )
476 	{
477 		set_error ( client, "invalid arguments (host must not be empty)" );
478 		return SPH_FALSE;
479 	}
480 
481 	unchain ( client, client->host );
482 	client->host = strchain ( client, host );
483 	client->port = port;
484 	return SPH_TRUE;
485 }
486 
487 
sphinx_set_connect_timeout(sphinx_client * client,float seconds)488 sphinx_bool sphinx_set_connect_timeout ( sphinx_client * client, float seconds )
489 {
490 	if ( !client )
491 		return SPH_FALSE;
492 
493 	client->timeout = seconds;
494 	return SPH_TRUE;
495 }
496 
497 
sphinx_set_limits(sphinx_client * client,int offset,int limit,int max_matches,int cutoff)498 sphinx_bool sphinx_set_limits ( sphinx_client * client, int offset, int limit, int max_matches, int cutoff )
499 {
500 	if ( !client || offset<0 || limit<=0 || max_matches<0 || cutoff<0 )
501 	{
502 		if ( offset<0 )				set_error ( client, "invalid arguments (offset must be >= 0)" );
503 		else if ( limit<=0 )		set_error ( client, "invalid arguments (limit must be > 0)" );
504 		else if ( max_matches<0 )	set_error ( client, "invalid arguments (max_matches must be >= 0)" );
505 		else if ( cutoff<0 )		set_error ( client, "invalid arguments (cutoff must be >= 0)" );
506 		else						set_error ( client, "invalid arguments" );
507 		return SPH_FALSE;
508 	}
509 
510 	client->offset = offset;
511 	client->limit = limit;
512 	if ( max_matches>0 )
513 		client->max_matches = max_matches;
514 	if ( cutoff>=0 )
515 		client->cutoff = cutoff;
516 	return SPH_TRUE;
517 }
518 
519 
sphinx_set_max_query_time(sphinx_client * client,int max_query_time)520 sphinx_bool sphinx_set_max_query_time ( sphinx_client * client, int max_query_time )
521 {
522 	if ( !client || max_query_time<=0 )
523 	{
524 		set_error ( client, "invalid arguments (max_query_time must be > 0)" );
525 		return SPH_FALSE;
526 	}
527 
528 	client->max_query_time = max_query_time;
529 	return SPH_TRUE;
530 }
531 
532 // DEPRECATED
sphinx_set_match_mode(sphinx_client * client,int mode)533 sphinx_bool sphinx_set_match_mode ( sphinx_client * client, int mode )
534 {
535 	fprintf ( stderr, "DEPRECATED: Do not call this method or, even better, use SphinxQL instead of an API\n" );
536 
537 	if ( !client || mode<SPH_MATCH_ALL || mode>SPH_MATCH_EXTENDED2 ) // FIXME?
538 	{
539 		set_error ( client, "invalid arguments (matching mode %d out of bounds)", mode );
540 		return SPH_FALSE;
541 	}
542 
543 	client->mode = mode;
544 	return SPH_TRUE;
545 }
546 
547 
sphinx_set_ranking_mode(sphinx_client * client,int ranker,const char * rankexpr)548 sphinx_bool sphinx_set_ranking_mode ( sphinx_client * client, int ranker, const char * rankexpr )
549 {
550 	if ( !client || ranker<SPH_RANK_PROXIMITY_BM25 || ranker>=SPH_RANK_TOTAL ) // FIXME?
551 	{
552 		set_error ( client, "invalid arguments (ranking mode %d out of bounds)", ranker );
553 		return SPH_FALSE;
554 	}
555 
556 	client->ranker = ranker;
557 	client->rankexpr = strchain ( client, rankexpr );
558 	return SPH_TRUE;
559 }
560 
561 
sphinx_set_sort_mode(sphinx_client * client,int mode,const char * sortby)562 sphinx_bool sphinx_set_sort_mode ( sphinx_client * client, int mode, const char * sortby )
563 {
564 	if ( !client
565 		|| mode<SPH_SORT_RELEVANCE
566 		|| mode>SPH_SORT_EXPR
567 		|| ( mode!=SPH_SORT_RELEVANCE && ( !sortby || !sortby[0] ) ) )
568 	{
569 		if ( mode<SPH_SORT_RELEVANCE || mode>SPH_SORT_EXPR )
570 		{
571 			set_error ( client, "invalid arguments (sorting mode %d out of bounds)", mode );
572 
573 		} else if ( mode!=SPH_SORT_RELEVANCE && ( !sortby || !sortby[0] ) )
574 		{
575 			set_error ( client, "invalid arguments (sortby clause must not be empty)", mode );
576 
577 		} else
578 		{
579 			set_error ( client, "invalid arguments", mode );
580 		}
581 		return SPH_FALSE;
582 	}
583 
584 	client->sort = mode;
585 	unchain ( client, client->sortby );
586 	client->sortby = strchain ( client, sortby );
587 	return SPH_TRUE;
588 }
589 
590 
sphinx_set_field_weights(sphinx_client * client,int num_weights,const char ** field_names,const int * field_weights)591 sphinx_bool sphinx_set_field_weights ( sphinx_client * client, int num_weights, const char ** field_names, const int * field_weights )
592 {
593 	int i;
594 
595 	if ( !client || num_weights<=0 || !field_names || !field_weights )
596 	{
597 		if ( num_weights<=0 )		set_error ( client, "invalid arguments (num_weights must be > 0)" );
598 		else if ( !field_names )	set_error ( client, "invalid arguments (field_names must not be NULL)" );
599 		else if ( !field_weights )	set_error ( client, "invalid arguments (field_weights must not be NULL)" );
600 		else						set_error ( client, "invalid arguments" );
601 		return SPH_FALSE;
602 	}
603 
604 	if ( client->copy_args )
605 	{
606 		for ( i=0; i<client->num_field_weights; i++ )
607 			unchain ( client, client->field_weights_names[i] );
608 		unchain ( client, client->field_weights_names );
609 		unchain ( client, client->field_weights_values );
610 
611 		field_names = chain ( client, field_names, num_weights*sizeof(const char*) );
612 		for ( i=0; i<num_weights; i++ )
613 			field_names[i] = strchain ( client, field_names[i] );
614 		field_weights = chain ( client, field_weights, num_weights*sizeof(int) );
615 	}
616 
617 	client->num_field_weights = num_weights;
618 	client->field_weights_names = field_names;
619 	client->field_weights_values = field_weights;
620 	return SPH_TRUE;
621 }
622 
623 
sphinx_set_index_weights(sphinx_client * client,int num_weights,const char ** index_names,const int * index_weights)624 sphinx_bool sphinx_set_index_weights ( sphinx_client * client, int num_weights, const char ** index_names, const int * index_weights )
625 {
626 	int i;
627 
628 	if ( !client || num_weights<=0 || !index_names || !index_weights )
629 	{
630 		if ( num_weights<=0 )		set_error ( client, "invalid arguments (num_weights must be > 0)" );
631 		else if ( !index_names )	set_error ( client, "invalid arguments (index_names must not be NULL)" );
632 		else if ( !index_weights )	set_error ( client, "invalid arguments (index_weights must not be NULL)" );
633 		else						set_error ( client, "invalid arguments" );
634 		return SPH_FALSE;
635 	}
636 
637 	if ( client->copy_args )
638 	{
639 		for ( i=0; i<client->num_index_weights; i++ )
640 			unchain ( client, client->index_weights_names[i] );
641 		unchain ( client, client->index_weights_names );
642 		unchain ( client, client->index_weights_values );
643 
644 		index_names = chain ( client, index_names, num_weights*sizeof(const char*) );
645 		for ( i=0; i<num_weights; i++ )
646 			index_names[i] = strchain ( client, index_names[i] );
647 		index_weights = chain ( client, index_weights, num_weights*sizeof(int) );
648 	}
649 
650 	client->num_index_weights = num_weights;
651 	client->index_weights_names = index_names;
652 	client->index_weights_values = index_weights;
653 	return SPH_TRUE;
654 }
655 
656 
sphinx_set_id_range(sphinx_client * client,sphinx_uint64_t minid,sphinx_uint64_t maxid)657 sphinx_bool sphinx_set_id_range ( sphinx_client * client, sphinx_uint64_t minid, sphinx_uint64_t maxid )
658 {
659 	if ( !client || minid>maxid )
660 	{
661 		set_error ( client, "invalid arguments (minid must be <= maxid)" );
662 		return SPH_FALSE;
663 	}
664 
665 	client->minid = minid;
666 	client->maxid = maxid;
667 	return SPH_TRUE;
668 }
669 
670 
sphinx_add_filter_entry(sphinx_client * client)671 static struct st_filter * sphinx_add_filter_entry ( sphinx_client * client )
672 {
673 	int len;
674 	if ( client->num_filters>=client->max_filters )
675 	{
676 		client->max_filters = ( client->max_filters<=0 ) ? client->num_filters + 8 : 2*client->max_filters;
677 		len = client->max_filters*sizeof(struct st_filter);
678 		client->filters = realloc ( client->filters, len );
679 		if ( !client->filters )
680 		{
681 			set_error ( client, "realloc() failed (bytes=%d)", len );
682 			return NULL;
683 		}
684 	}
685 	return client->filters + client->num_filters++;
686 }
687 
688 
sphinx_add_filter(sphinx_client * client,const char * attr,int num_values,const sphinx_int64_t * values,sphinx_bool exclude)689 sphinx_bool sphinx_add_filter ( sphinx_client * client, const char * attr, int num_values, const sphinx_int64_t * values, sphinx_bool exclude )
690 {
691 	struct st_filter * filter;
692 
693 	if ( !client || !attr || num_values<=0 || !values )
694 	{
695 		if ( !attr )				set_error ( client, "invalid arguments (attr must not be empty)" );
696 		else if ( num_values<=0 )	set_error ( client, "invalid arguments (num_values must be > 0)" );
697 		else if ( !values )			set_error ( client, "invalid arguments (values must not be NULL)" );
698 		else						set_error ( client, "invalid arguments" );
699 		return SPH_FALSE;
700 	}
701 
702 	filter = sphinx_add_filter_entry ( client );
703 	if ( !filter )
704 		return SPH_FALSE;
705 
706 	filter->attr = strchain ( client, attr );
707 	filter->filter_type = SPH_FILTER_VALUES;
708 	filter->num_values = num_values;
709 	filter->values = chain ( client, values, num_values*sizeof(sphinx_int64_t) );
710 	filter->exclude = exclude;
711 	return SPH_TRUE;
712 }
713 
714 
sphinx_add_filter_string(sphinx_client * client,const char * attr,const char * value,sphinx_bool exclude)715 sphinx_bool sphinx_add_filter_string ( sphinx_client * client, const char * attr, const char * value, sphinx_bool exclude )
716 {
717 	struct st_filter * filter;
718 
719 	if ( !client || !attr || !value )
720 	{
721 		if ( !attr )				set_error ( client, "invalid arguments (attr must not be empty)" );
722 		else if ( !value )			set_error ( client, "invalid arguments (value must not be empty)" );
723 		else						set_error ( client, "invalid arguments" );
724 		return SPH_FALSE;
725 	}
726 
727 	filter = sphinx_add_filter_entry ( client );
728 	if ( !filter )
729 		return SPH_FALSE;
730 
731 	filter->attr = strchain ( client, attr );
732 	filter->filter_type = SPH_FILTER_STRING;
733 	filter->svalue = strchain ( client, value );
734 	filter->exclude = exclude;
735 	return SPH_TRUE;
736 }
737 
738 
sphinx_add_filter_range(sphinx_client * client,const char * attr,sphinx_int64_t umin,sphinx_int64_t umax,sphinx_bool exclude)739 sphinx_bool sphinx_add_filter_range ( sphinx_client * client, const char * attr, sphinx_int64_t umin, sphinx_int64_t umax, sphinx_bool exclude )
740 {
741 	struct st_filter * filter;
742 
743 	if ( !client || !attr || umin>umax )
744 	{
745 		if ( !attr )			set_error ( client, "invalid arguments (attr must not be empty)" );
746 		else if ( umin>umax )	set_error ( client, "invalid arguments (umin must be <= umax)" );
747 		else					set_error ( client, "invalid arguments" );
748 		return SPH_FALSE;
749 	}
750 
751 	filter = sphinx_add_filter_entry ( client );
752 	if ( !filter )
753 		return SPH_FALSE;
754 
755 	filter->attr = strchain ( client, attr );
756 	filter->filter_type = SPH_FILTER_RANGE;
757 	filter->umin = umin;
758 	filter->umax = umax;
759 	filter->exclude = exclude;
760 	return SPH_TRUE;
761 }
762 
763 
sphinx_add_filter_float_range(sphinx_client * client,const char * attr,float fmin,float fmax,sphinx_bool exclude)764 sphinx_bool sphinx_add_filter_float_range ( sphinx_client * client, const char * attr, float fmin, float fmax, sphinx_bool exclude )
765 {
766 	struct st_filter * filter;
767 
768 	if ( !client || !attr || fmin>fmax )
769 	{
770 		if ( !attr )			set_error ( client, "invalid arguments (attr must not be empty)" );
771 		else if ( fmin>fmax )	set_error ( client, "invalid arguments (fmin must be <= fmax)" );
772 		else					set_error ( client, "invalid arguments" );
773 		return SPH_FALSE;
774 	}
775 
776 	filter = sphinx_add_filter_entry ( client );
777 	if ( !filter )
778 		return SPH_FALSE;
779 
780 	filter->attr = strchain ( client, attr );
781 	filter->filter_type = SPH_FILTER_FLOATRANGE;
782 	filter->fmin = fmin;
783 	filter->fmax = fmax;
784 	filter->exclude = exclude;
785 	return SPH_TRUE;
786 }
787 
788 
sphinx_set_geoanchor(sphinx_client * client,const char * attr_latitude,const char * attr_longitude,float latitude,float longitude)789 sphinx_bool sphinx_set_geoanchor ( sphinx_client * client, const char * attr_latitude, const char * attr_longitude, float latitude, float longitude )
790 {
791 	if ( !client || !attr_latitude || !attr_latitude[0] || !attr_longitude || !attr_longitude[0] )
792 	{
793 		if ( !attr_latitude || !attr_latitude[0] )			set_error ( client, "invalid arguments (attr_latitude must not be empty)" );
794 		else if ( !attr_longitude || !attr_longitude[0] )	set_error ( client, "invalid arguments (attr_longitude must not be empty)" );
795 		else												set_error ( client, "invalid arguments" );
796 		return SPH_FALSE;
797 	}
798 
799 	unchain ( client, client->geoanchor_attr_lat );
800 	unchain ( client, client->geoanchor_attr_long );
801 	client->geoanchor_attr_lat = strchain ( client, attr_latitude );
802 	client->geoanchor_attr_long = strchain ( client, attr_longitude );
803 	client->geoanchor_lat = latitude;
804 	client->geoanchor_long = longitude;
805 	return SPH_TRUE;
806 }
807 
808 
sphinx_set_groupby(sphinx_client * client,const char * attr,int groupby_func,const char * group_sort)809 sphinx_bool sphinx_set_groupby ( sphinx_client * client, const char * attr, int groupby_func, const char * group_sort )
810 {
811 	if ( !client || !attr || groupby_func<SPH_GROUPBY_DAY || groupby_func>SPH_GROUPBY_ATTRPAIR )
812 	{
813 		if ( !attr )
814 		{
815 			set_error ( client, "invalid arguments (attr must not be empty)" );
816 
817 		} else if ( groupby_func<SPH_GROUPBY_DAY || groupby_func>SPH_GROUPBY_ATTRPAIR )
818 		{
819 			set_error ( client, "invalid arguments (groupby_func %d out of bounds)", groupby_func );
820 
821 		} else
822 		{
823 			set_error ( client, "invalid arguments" );
824 		}
825 		return SPH_FALSE;
826 	}
827 
828 	unchain ( client, client->group_by );
829 	unchain ( client, client->group_sort );
830 	client->group_by = strchain ( client, attr );
831 	client->group_func = groupby_func;
832 	client->group_sort = strchain ( client, group_sort ? group_sort : "@groupby desc" );
833 	return SPH_TRUE;
834 }
835 
836 
sphinx_set_groupby_distinct(sphinx_client * client,const char * attr)837 sphinx_bool sphinx_set_groupby_distinct ( sphinx_client * client, const char * attr )
838 {
839 	if ( !client || !attr )
840 	{
841 		if ( !attr )	set_error ( client, "invalid arguments (attr must not be empty)" );
842 		else			set_error ( client, "invalid arguments" );
843 		return SPH_FALSE;
844 	}
845 
846 	unchain ( client, client->group_distinct );
847 	client->group_distinct = strchain ( client, attr );
848 	return SPH_TRUE;
849 }
850 
851 
sphinx_set_retries(sphinx_client * client,int count,int delay)852 sphinx_bool sphinx_set_retries ( sphinx_client * client, int count, int delay )
853 {
854 	if ( !client || count<0 || count>1000 || delay<0 || delay>100000 )
855 	{
856 		if ( count<0 || count>1000 )		set_error ( client, "invalid arguments (count value %d out of bounds)", count );
857 		else if ( delay<0 || delay>100000 )	set_error ( client, "invalid arguments (delay value %d out of bounds)", delay );
858 		else								set_error ( client, "invalid arguments" );
859 		return SPH_FALSE;
860 	}
861 
862 	client->retry_count = count;
863 	client->retry_delay = delay;
864 	return SPH_TRUE;
865 }
866 
867 // DEPRECATED
sphinx_add_override(sphinx_client * client,const char * attr,const sphinx_uint64_t * docids,int num_values,const unsigned int * values)868 sphinx_bool sphinx_add_override ( sphinx_client * client, const char * attr, const sphinx_uint64_t * docids, int num_values, const unsigned int * values )
869 {
870 	struct st_override * p;
871 
872 	fprintf ( stderr, "DEPRECATED: Do not call this method. Use SphinxQL REMAP() function instead.\n" );
873 
874 	if ( !client )
875 		return SPH_FALSE;
876 
877 	if ( client->ver_search<0x115 )
878 	{
879 		set_error ( client, "sphinx_add_override not supported by chosen protocol version" );
880 		return SPH_FALSE;
881 	}
882 
883 	if ( client->num_overrides>=client->max_overrides )
884 	{
885 		client->max_overrides = ( client->max_overrides<=0 ) ? 8 : 2*client->max_overrides;
886 		client->overrides = realloc ( client->overrides, client->max_overrides *sizeof(struct st_override) );
887 	}
888 
889 	p = client->overrides + client->num_overrides;
890 	client->num_overrides++;
891 
892 	p->attr = strchain ( client, attr );
893 	p->docids = chain ( client, docids, sizeof(sphinx_uint64_t)*num_values );
894 	p->num_values = num_values;
895 	p->uint_values = chain ( client, values, sizeof(unsigned int)*num_values );
896 	return SPH_TRUE;
897 }
898 
899 
sphinx_set_select(sphinx_client * client,const char * select_list)900 sphinx_bool sphinx_set_select ( sphinx_client * client, const char * select_list )
901 {
902 	if ( !client )
903 		return SPH_FALSE;
904 
905 	if ( client->ver_search<0x116 )
906 	{
907 		set_error ( client, "sphinx_set_select not supported by chosen protocol version" );
908 		return SPH_FALSE;
909 	}
910 
911 	unchain ( client, client->select_list );
912 	client->select_list = strchain ( client, select_list );
913 	return SPH_TRUE;
914 }
915 
916 
set_bit(int * flags,int bit,sphinx_bool enable)917 void set_bit ( int * flags, int bit, sphinx_bool enable )
918 {
919 	int bit_mask = 1<<bit;
920 	if ( enable )
921 		*flags |= bit_mask;
922 	else
923 		*flags &= ( 0xff ^ bit_mask );
924 }
925 
926 
sphinx_set_query_flags(sphinx_client * client,const char * flag_name,sphinx_bool enabled,int max_predicted_msec)927 sphinx_bool sphinx_set_query_flags ( sphinx_client * client, const char * flag_name, sphinx_bool enabled, int max_predicted_msec )
928 {
929 	if ( !client )
930 		return SPH_FALSE;
931 
932 	if ( client->ver_search<0x11B )
933 	{
934 		set_error ( client, "sphinx_set_query_flags not supported by chosen protocol version" );
935 		return SPH_FALSE;
936 	}
937 
938 	if ( !flag_name || !flag_name[0] )
939 	{
940 		set_error ( client, "invalid arguments (empty flag_name)" );
941 		return SPH_FALSE;
942 	}
943 	if ( strcmp ( flag_name, "max_predicted_time")==0 && max_predicted_msec<0 )
944 	{
945 		set_error ( client, "invalid arguments (max_predicted_time must be >0)" );
946 		return SPH_FALSE;
947 	}
948 
949 	if ( strcmp ( flag_name, "reverse_scan")==0 )
950 	{
951 		set_bit ( &client->query_flags, 0, enabled );
952 	} else if ( strcmp ( flag_name, "sort_method_kbuffer")==0 )
953 	{
954 			set_bit ( &client->query_flags, 1, enabled );
955 	} else if ( strcmp ( flag_name, "max_predicted_time")==0 )
956 	{
957 		client->predicted_time = max_predicted_msec;
958 		set_bit ( &client->query_flags, 2, max_predicted_msec>0 );
959 	} else if ( strcmp ( flag_name, "boolean_simplify")==0 )
960 	{
961 		set_bit ( &client->query_flags, 3, enabled );
962 	} else if ( strcmp ( flag_name, "idf_plain")==0 )
963 	{
964 		set_bit ( &client->query_flags, 4, enabled );
965 	} else if ( strcmp ( flag_name, "global_idf")==0 )
966 	{
967 		set_bit ( &client->query_flags, 5, enabled );
968 	} else if ( strcmp ( flag_name, "tfidf_normalized")==0 )
969 	{
970 		set_bit ( &client->query_flags, 6, enabled );
971 	} else
972 	{
973 		set_error ( client, "invalid arguments (unknown flag_name)" );
974 		return SPH_FALSE;
975 	}
976 
977 	return SPH_TRUE;
978 }
979 
980 
sphinx_reset_query_flags(sphinx_client * client)981 void sphinx_reset_query_flags ( sphinx_client * client )
982 {
983 	client->query_flags = 1<<6;
984 	client->predicted_time = 0;
985 }
986 
987 
sphinx_set_outer_select(sphinx_client * client,const char * orderby,int offset,int limit)988 sphinx_bool sphinx_set_outer_select ( sphinx_client * client, const char * orderby, int offset, int limit )
989 {
990 	if ( !client )
991 		return SPH_FALSE;
992 
993 	if ( client->ver_search<0x11D )
994 	{
995 		set_error ( client, "sphinx_set_outer not supported by chosen protocol version" );
996 		return SPH_FALSE;
997 	}
998 
999 	unchain ( client, client->outer_orderby );
1000 	client->outer_orderby = strchain ( client, orderby );
1001 	client->outer_offset = offset;
1002 	client->outer_limit = limit;
1003 	client->has_outer = SPH_TRUE;
1004 	return SPH_TRUE;
1005 }
1006 
1007 
sphinx_reset_outer_select(sphinx_client * client)1008 void sphinx_reset_outer_select ( sphinx_client * client )
1009 {
1010 	if ( !client )
1011 		return;
1012 
1013 	unchain ( client, client->outer_orderby );
1014 	client->outer_orderby = NULL;
1015 	client->outer_offset = 0;
1016 	client->outer_limit = 0;
1017 	client->has_outer = SPH_FALSE;
1018 }
1019 
1020 
sphinx_reset_filters(sphinx_client * client)1021 void sphinx_reset_filters ( sphinx_client * client )
1022 {
1023 	int i;
1024 
1025 	if ( !client )
1026 		return;
1027 
1028 	if ( client->filters )
1029 	{
1030 		if ( client->copy_args )
1031 			for ( i=0; i<client->num_filters; i++ )
1032 		{
1033 			unchain ( client, client->filters[i].attr );
1034 			if ( client->filters[i].filter_type==SPH_FILTER_VALUES )
1035 				unchain ( client, client->filters[i].values );
1036 			if ( client->filters[i].filter_type==SPH_FILTER_STRING )
1037 				unchain ( client, client->filters[i].svalue );
1038 		}
1039 
1040 		free ( client->filters );
1041 		client->filters = NULL;
1042 	}
1043 	client->num_filters = client->max_filters = 0;
1044 }
1045 
1046 
sphinx_reset_groupby(sphinx_client * client)1047 void sphinx_reset_groupby ( sphinx_client * client )
1048 {
1049 	if ( !client )
1050 		return;
1051 
1052 	unchain ( client, client->group_by );
1053 	unchain ( client, client->group_sort );
1054 	client->group_by = NULL;
1055 	client->group_func = SPH_GROUPBY_ATTR;
1056 	client->group_sort = strchain ( client, "@groupby desc" );
1057 	client->group_distinct = NULL;
1058 }
1059 
1060 //////////////////////////////////////////////////////////////////////////
1061 
sphinx_dismiss_requests(sphinx_client * client)1062 static int sphinx_dismiss_requests ( sphinx_client * client )
1063 {
1064 	int nreqs = client->num_reqs, i;
1065 	for ( i=0; i<client->num_reqs; i++ )
1066 		free ( client->reqs[i] );
1067 	client->num_reqs = 0;
1068 	return nreqs;
1069 }
1070 
1071 
sphinx_query(sphinx_client * client,const char * query,const char * index_list,const char * comment)1072 sphinx_result * sphinx_query ( sphinx_client * client, const char * query, const char * index_list, const char * comment )
1073 {
1074 	sphinx_result * res;
1075 
1076 	if ( !client )
1077 		return NULL;
1078 
1079 	if ( client->num_reqs!=0 )
1080 	{
1081 		set_error ( client, "sphinx_query() must not be called after sphinx_add_query()" );
1082 		return NULL;
1083 	}
1084 
1085 	if ( sphinx_add_query ( client, query, index_list, comment )!=0 )
1086 		return NULL;
1087 
1088 	res = sphinx_run_queries ( client ); // just a shortcut for client->results[0]
1089 	sphinx_dismiss_requests ( client ); // sphinx_query() is fire and forget; dismiss request in all cases
1090 	if ( !res )
1091 		return NULL;
1092 
1093 	client->error = res->error;
1094 	client->warning = res->warning;
1095 	return ( res->status==SEARCHD_ERROR ) ? NULL : res;
1096 }
1097 
1098 
safestrlen(const char * s)1099 static size_t safestrlen ( const char * s )
1100 {
1101 	return s ? strlen(s) : 0;
1102 }
1103 
1104 
calc_req_len(sphinx_client * client,const char * query,const char * index_list,const char * comment)1105 static int calc_req_len ( sphinx_client * client, const char * query, const char * index_list, const char * comment )
1106 {
1107 	int i, filter_val_size;
1108 	size_t res;
1109 
1110 	res = 96 + 2*(int)sizeof(sphinx_uint64_t) + 4*client->num_weights
1111 		+ safestrlen ( client->sortby )
1112 		+ safestrlen ( query )
1113 		+ safestrlen ( index_list )
1114 		+ safestrlen ( client->group_by )
1115 		+ safestrlen ( client->group_sort )
1116 		+ safestrlen ( client->group_distinct )
1117 		+ safestrlen ( comment )
1118 		+ ( ( client->ranker==SPH_RANK_EXPR ) ? ( 4 + safestrlen ( client->rankexpr ) ) : 0 );
1119 
1120 	filter_val_size = ( client->ver_search>=0x114 ) ? 8 : 4;
1121 	for ( i=0; i<client->num_filters; i++ )
1122 	{
1123 		const struct st_filter * filter = &client->filters[i];
1124 		res += 12 + safestrlen ( filter->attr ); // string attr-name; int type; int exclude-flag
1125 
1126 		switch ( filter->filter_type )
1127 		{
1128 			case SPH_FILTER_VALUES:		res += 4 + filter_val_size*filter->num_values; break; // int values-count; uint32/int64[] values
1129 			case SPH_FILTER_RANGE:		res += 2*filter_val_size; break; // uint32/int64 min-val, max-val
1130 			case SPH_FILTER_FLOATRANGE:	res += 8; break; // float min-val,max-val
1131 			case SPH_FILTER_STRING:		res += 4 + safestrlen ( filter->svalue ); break;
1132 		}
1133 	}
1134 
1135 	if ( client->geoanchor_attr_lat && client->geoanchor_attr_long )
1136 		res += 16 + safestrlen ( client->geoanchor_attr_lat ) + safestrlen ( client->geoanchor_attr_long ); // string lat-attr, long-attr; float lat, long
1137 
1138 	for ( i=0; i<client->num_index_weights; i++ )
1139 		res += 8 + safestrlen ( client->index_weights_names[i] ); // string index-name; int index-weight
1140 
1141 	for ( i=0; i<client->num_field_weights; i++ )
1142 		res += 8 + safestrlen ( client->field_weights_names[i] ); // string field-name; int field-weight
1143 
1144 	if ( client->ver_search>=0x115 )
1145 	{
1146 		res += 4; // int overrides-count
1147 		for ( i=0; i<client->num_overrides; i++ )
1148 		{
1149 			res += 8 + safestrlen ( client->overrides[i].attr ); // string attr, int attr-type
1150 			res += 4 + 12*client->overrides[i].num_values; // int values-count, { uint64 docid, uint32 value }[] override
1151 		}
1152 	}
1153 
1154 	if ( client->ver_search>=0x116 )
1155 		res += 4 + safestrlen ( client->select_list ); // string select_list
1156 
1157 	if ( client->ver_search>=0x11B )
1158 		res += 4 + ( client->predicted_time>0 ? 4 : 0 );
1159 
1160 	if ( client->ver_search>=0x11D )
1161 		res += safestrlen ( client->outer_orderby ) + 16; // string outer order by + int outer offset + int outer limit + has outer flag
1162 
1163 	return (int)res;
1164 }
1165 
1166 
send_bytes(char ** pp,const char * bytes,int len)1167 static void send_bytes ( char ** pp, const char * bytes, int len )
1168 {
1169 	char * ptr;
1170 	int i;
1171 
1172 	ptr = *pp;
1173 	if ( ptr )
1174 		for ( i=0; i<len; i++ )
1175 			*ptr++ = bytes[i];
1176 	*pp = ptr;
1177 }
1178 
1179 
send_int(char ** pp,unsigned int value)1180 static void send_int ( char ** pp, unsigned int value )
1181 {
1182 	unsigned char * b = (unsigned char*) *pp;
1183 	b[0] = ( value >> 24 ) & 0xff;
1184 	b[1] = ( value >> 16 ) & 0xff;
1185 	b[2] = ( value >> 8 ) & 0xff;
1186 	b[3] = ( value & 0xFF );
1187 	*pp += 4;
1188 }
1189 
1190 
send_word(char ** pp,unsigned short value)1191 static void send_word ( char ** pp, unsigned short value )
1192 {
1193 	unsigned char * b = (unsigned char*) *pp;
1194 	b[0] = ( value >> 8 );
1195 	b[1] = ( value & 0xFF );
1196 	*pp += 2;
1197 }
1198 
1199 
send_str(char ** pp,const char * s)1200 static void send_str ( char ** pp, const char * s )
1201 {
1202 	int len;
1203 	len = s ? (int)strlen(s) : 0;
1204 	send_int ( pp, len );
1205 	send_bytes ( pp, s, len );
1206 }
1207 
1208 
send_qword(char ** pp,sphinx_uint64_t value)1209 static void send_qword ( char ** pp, sphinx_uint64_t value )
1210 {
1211 	send_int ( pp, (int)( value >> 32 ) );
1212 	send_int ( pp, (int)( value & ((sphinx_uint64_t)0xffffffffL) ) );
1213 }
1214 
1215 
send_float(char ** pp,float value)1216 static void send_float ( char ** pp, float value )
1217 {
1218 	union
1219 	{
1220 		float f;
1221 		int i;
1222 	} u;
1223 
1224 	u.f = value;
1225 	send_int ( pp, u.i );
1226 }
1227 
1228 
sphinx_add_query(sphinx_client * client,const char * query,const char * index_list,const char * comment)1229 int sphinx_add_query ( sphinx_client * client, const char * query, const char * index_list, const char * comment )
1230 {
1231 	int i, j, req_len;
1232 	char * req;
1233 
1234 	if ( client->num_reqs<0 || client->num_reqs>=MAX_REQS )
1235 	{
1236 		set_error ( client, "num_reqs=%d out of bounds (too many queries?)", client->num_reqs );
1237 		return -1;
1238 	}
1239 
1240 	req_len = calc_req_len ( client, query, index_list, comment );
1241 
1242 	req = malloc ( req_len );
1243 	if ( !req )
1244 	{
1245 		set_error ( client, "malloc() failed (bytes=%d)", req_len );
1246 		return -1;
1247 	}
1248 
1249 	client->reqs[client->num_reqs] = req;
1250 	client->req_lens[client->num_reqs] = req_len;
1251 	client->num_reqs++;
1252 
1253 	if ( client->ver_search>=0x11B )
1254 		send_int ( &req, client->query_flags );
1255 
1256 	send_int ( &req, client->offset );
1257 	send_int ( &req, client->limit );
1258 	send_int ( &req, client->mode );
1259 	send_int ( &req, client->ranker );
1260 	if ( client->ranker==SPH_RANK_EXPR )
1261 		send_str ( &req, client->rankexpr );
1262 	send_int ( &req, client->sort );
1263 	send_str ( &req, client->sortby );
1264 	send_str ( &req, query );
1265 	send_int ( &req, client->num_weights );
1266 	for ( i=0; i<client->num_weights; i++ )
1267 		send_int ( &req, client->weights[i] );
1268 	send_str ( &req, index_list );
1269 	send_int ( &req, 1 ); // id range bits
1270 	send_qword ( &req, client->minid );
1271 	send_qword ( &req, client->maxid );
1272 	send_int ( &req, client->num_filters );
1273 	for ( i=0; i<client->num_filters; i++ )
1274 	{
1275 		send_str ( &req, client->filters[i].attr );
1276 		send_int ( &req, client->filters[i].filter_type );
1277 
1278 		switch ( client->filters[i].filter_type )
1279 		{
1280 			case SPH_FILTER_VALUES:
1281 				send_int ( &req, client->filters[i].num_values );
1282 				if ( client->ver_search>=0x114 )
1283 				{
1284 					for ( j=0; j<client->filters[i].num_values; j++ )
1285 						send_qword ( &req, client->filters[i].values[j] );
1286 				} else
1287 				{
1288 					for ( j=0; j<client->filters[i].num_values; j++ )
1289 						send_int ( &req, (unsigned int)client->filters[i].values[j] );
1290 				}
1291 				break;
1292 
1293 			case SPH_FILTER_RANGE:
1294 				if ( client->ver_search>=0x114 )
1295 				{
1296 					send_qword ( &req, client->filters[i].umin );
1297 					send_qword ( &req, client->filters[i].umax );
1298 				} else
1299 				{
1300 					send_int ( &req, (unsigned int)client->filters[i].umin );
1301 					send_int ( &req, (unsigned int)client->filters[i].umax );
1302 				}
1303 				break;
1304 
1305 			case SPH_FILTER_FLOATRANGE:
1306 				send_float ( &req, client->filters[i].fmin );
1307 				send_float ( &req, client->filters[i].fmax );
1308 				break;
1309 
1310 			case SPH_FILTER_STRING:
1311 				send_str ( &req, client->filters[i].svalue );
1312 				break;
1313 		}
1314 
1315 		send_int ( &req, client->filters[i].exclude );
1316 	}
1317 	send_int ( &req, client->group_func );
1318 	send_str ( &req, client->group_by );
1319 	send_int ( &req, client->max_matches );
1320 	send_str ( &req, client->group_sort );
1321 	send_int ( &req, client->cutoff );
1322 	send_int ( &req, client->retry_count );
1323 	send_int ( &req, client->retry_delay );
1324 	send_str ( &req, client->group_distinct );
1325 	if ( client->geoanchor_attr_lat && client->geoanchor_attr_long )
1326 	{
1327 		send_int ( &req, 1 );
1328 		send_str ( &req, client->geoanchor_attr_lat );
1329 		send_str ( &req, client->geoanchor_attr_long );
1330 		send_float ( &req, client->geoanchor_lat );
1331 		send_float ( &req, client->geoanchor_long );
1332 	} else
1333 	{
1334 		send_int ( &req, 0 );
1335 	}
1336 	send_int ( &req, client->num_index_weights );
1337 	for ( i=0; i<client->num_index_weights; i++ )
1338 	{
1339 		send_str ( &req, client->index_weights_names[i] );
1340 		send_int ( &req, client->index_weights_values[i] );
1341 	}
1342 	send_int ( &req, client->max_query_time );
1343 	send_int ( &req, client->num_field_weights );
1344 	for ( i=0; i<client->num_field_weights; i++ )
1345 	{
1346 		send_str ( &req, client->field_weights_names[i] );
1347 		send_int ( &req, client->field_weights_values[i] );
1348 	}
1349 	send_str ( &req, comment );
1350 
1351 	if ( client->ver_search>=0x115 )
1352 	{
1353 		send_int ( &req, client->num_overrides );
1354 		for ( i=0; i<client->num_overrides; i++ )
1355 		{
1356 			send_str ( &req, client->overrides[i].attr );
1357 			send_int ( &req, SPH_ATTR_INTEGER );
1358 			send_int ( &req, client->overrides[i].num_values );
1359 			for ( j=0; j<client->overrides[i].num_values; j++ )
1360 			{
1361 				send_qword ( &req, client->overrides[i].docids[j] );
1362 				send_int ( &req, client->overrides[i].uint_values[j] );
1363 			}
1364 		}
1365 	}
1366 
1367 	if ( client->ver_search>=0x116 )
1368 		send_str ( &req, client->select_list );
1369 
1370 	if ( client->ver_search>=0x11B && client->predicted_time>0 )
1371 		send_int ( &req, client->predicted_time );
1372 
1373 	if ( client->ver_search>=0x11D )
1374 	{
1375 		send_str ( &req, client->outer_orderby );
1376 		send_int ( &req, client->outer_offset );
1377 		send_int ( &req, client->outer_limit );
1378 		send_int ( &req, client->has_outer );
1379 	}
1380 
1381 	if ( !req )
1382 	{
1383 		set_error ( client, "internal error, failed to build request" );
1384 		free ( client->reqs [ --client->num_reqs ] );
1385 		return -1;
1386 	}
1387 
1388 	return client->num_reqs-1;
1389 }
1390 
1391 
sock_error()1392 static const char * sock_error ()
1393 {
1394 #if _WIN32
1395 	static char sBuf [ 256 ];
1396 	int iErr;
1397 
1398 	iErr = WSAGetLastError ();
1399 	_snprintf ( sBuf, sizeof(sBuf), "WSA error %d", iErr );
1400 	return sBuf;
1401 #else
1402 	return strerror ( errno );
1403 #endif
1404 }
1405 
1406 
sock_errno()1407 static int sock_errno ()
1408 {
1409 #ifdef _WIN32
1410 	return WSAGetLastError ();
1411 #else
1412 	return errno;
1413 #endif
1414 }
1415 
1416 
sock_set_nonblocking(int sock)1417 static int sock_set_nonblocking ( int sock )
1418 {
1419 #if _WIN32
1420 	u_long uMode = 1;
1421 	return ioctlsocket ( sock, FIONBIO, &uMode );
1422 #else
1423 	return fcntl ( sock, F_SETFL, O_NONBLOCK );
1424 #endif
1425 }
1426 
1427 
sock_set_blocking(int sock)1428 static int sock_set_blocking ( int sock )
1429 {
1430 #if _WIN32
1431 	u_long uMode = 0;
1432 	return ioctlsocket ( sock, FIONBIO, &uMode );
1433 #else
1434 	return fcntl ( sock, F_SETFL, 0 );
1435 #endif
1436 }
1437 
1438 
sock_close(int sock)1439 void sock_close ( int sock )
1440 {
1441 	if ( sock<0 )
1442 		return;
1443 
1444 #if _WIN32
1445 	closesocket ( sock );
1446 #else
1447 	close ( sock );
1448 #endif
1449 }
1450 
1451 
1452 // wrap FD_SET to prevent warnings on Windows
1453 #if _WIN32
1454 #pragma warning(disable:4127) // conditional expr is const
1455 #pragma warning(disable:4389) // signed/unsigned mismatch
SPH_FD_SET(int fd,fd_set * fdset)1456 void SPH_FD_SET ( int fd, fd_set * fdset ) { FD_SET ( fd, fdset ); }
1457 #pragma warning(default:4127) // conditional expr is const
1458 #pragma warning(default:4389) // signed/unsigned mismatch
1459 #else // !USE_WINDOWS
1460 #define	SPH_FD_SET FD_SET
1461 #endif
1462 
1463 
net_write(int fd,const char * bytes,int len,sphinx_client * client)1464 static sphinx_bool net_write ( int fd, const char * bytes, int len, sphinx_client * client )
1465 {
1466 	int res;
1467 #if defined(_WIN32) || defined(SO_NOSIGPIPE) || !defined(MSG_NOSIGNAL)
1468 	res = send ( fd, bytes, len, 0 );
1469 #else
1470 	res = send ( fd, bytes, len, MSG_NOSIGNAL );
1471 #endif
1472 
1473 	if ( res<0 )
1474 	{
1475 		set_error ( client, "send() error: %s", sock_error() );
1476 		return SPH_FALSE;
1477 	}
1478 
1479 	if ( res!=len )
1480 	{
1481 		set_error ( client, "send() error: incomplete write (len=%d, sent=%d)", len, res );
1482 		return SPH_FALSE;
1483 	}
1484 
1485 	return SPH_TRUE;
1486 }
1487 
1488 
net_read(int fd,char * buf,int len,sphinx_client * client)1489 static sphinx_bool net_read ( int fd, char * buf, int len, sphinx_client * client )
1490 {
1491 	int res, err;
1492 	for ( ;; )
1493 	{
1494 		res = recv ( fd, buf, len, 0 );
1495 
1496 		if ( res<0 )
1497 		{
1498 			err = sock_errno();
1499 			if ( err==EINTR || err==EWOULDBLOCK ) // FIXME! remove non-blocking mode here; add timeout
1500 				continue;
1501 			set_error ( client, "recv(): read error (error=%s)", sock_error() );
1502 			return SPH_FALSE;
1503 		}
1504 
1505 		len -= res;
1506 		buf += res;
1507 
1508 		if ( len==0 )
1509 			return SPH_TRUE;
1510 
1511 		if ( res==0 )
1512 		{
1513 			set_error ( client, "recv(): incomplete read (len=%d, recv=%d)", len, res );
1514 			return SPH_FALSE;
1515 		}
1516 	}
1517 }
1518 
1519 
net_create_inet_sock(sphinx_client * client)1520 static int net_create_inet_sock ( sphinx_client * client )
1521 {
1522 	struct hostent * hp;
1523 	struct sockaddr_in sa;
1524 	int sock, res, err, optval;
1525 
1526 	hp = gethostbyname ( client->host );
1527 	if ( !hp )
1528 	{
1529 		set_error ( client, "host name lookup failed (host=%s, error=%s)", client->host, sock_error() );
1530 		return -1;
1531 	}
1532 
1533 	memset ( &sa, 0, sizeof(sa) );
1534 	memcpy ( &sa.sin_addr, hp->h_addr_list[0], hp->h_length );
1535 	sa.sin_family = hp->h_addrtype;
1536 	sa.sin_port = htons ( (unsigned short)client->port );
1537 
1538 	sock = (int) socket ( hp->h_addrtype, SOCK_STREAM, 0 );
1539 	if ( sock<0 )
1540 	{
1541 		set_error ( client, "socket() failed: %s", sock_error() );
1542 		return -1;
1543 	}
1544 
1545 	if ( sock_set_nonblocking ( sock )<0 )
1546 	{
1547 		set_error ( client, "sock_set_nonblocking() failed: %s", sock_error() );
1548 		return -1;
1549 	}
1550 
1551 	optval = 1;
1552 #if defined(SO_NOSIGPIPE)
1553 	if ( setsockopt ( sock, SOL_SOCKET, SO_NOSIGPIPE, (void *)&optval, (socklen_t)sizeof(optval) ) < 0 )
1554 	{
1555 		set_error ( client, "setsockopt() failed: %s", sock_error() );
1556 		return -1;
1557 	}
1558 #endif
1559 
1560 	res = connect ( sock, (struct sockaddr*)&sa, sizeof(sa) );
1561 	if ( res==0 )
1562 		return sock;
1563 
1564 	err = sock_errno();
1565 #ifdef EINPROGRESS
1566 	if ( err!=EWOULDBLOCK && err!=EINPROGRESS )
1567 #else
1568 	if ( err!=EWOULDBLOCK )
1569 #endif
1570 	{
1571 		set_error ( client, "connect() failed: %s", sock_error() );
1572 		return -1;
1573 	}
1574 
1575 	return sock;
1576 }
1577 
1578 #ifndef _WIN32
net_create_unix_sock(sphinx_client * client)1579 static int net_create_unix_sock ( sphinx_client * client )
1580 {
1581 	struct hostent * hp;
1582 	struct sockaddr_un uaddr;
1583 	int sock, res, err, optval, len;
1584 
1585 	len = strlen ( client->host );
1586 
1587 	if ( len + 1 > sizeof( uaddr.sun_path ) )
1588 		set_error ( client, "UNIX socket path is too long (len=%d)", len );
1589 
1590 	memset ( &uaddr, 0, sizeof(uaddr) );
1591 	uaddr.sun_family = AF_UNIX;
1592 	memcpy ( uaddr.sun_path, client->host, len + 1 );
1593 
1594 	sock = socket ( AF_UNIX, SOCK_STREAM, 0 );
1595 	if ( sock<0 )
1596 	{
1597 		set_error ( client, "UNIX socket() failed: %s", sock_error() );
1598 		return -1;
1599 	}
1600 
1601 	if ( sock_set_nonblocking ( sock )<0 )
1602 	{
1603 		set_error ( client, "sock_set_nonblocking() failed: %s", sock_error() );
1604 		return -1;
1605 	}
1606 
1607 	optval = 1;
1608 #if defined(SO_NOSIGPIPE)
1609 	if ( setsockopt ( sock, SOL_SOCKET, SO_NOSIGPIPE, (void *)&optval, (socklen_t)sizeof(optval) ) < 0 )
1610 	{
1611 		set_error ( client, "setsockopt() failed: %s", sock_error() );
1612 		return -1;
1613 	}
1614 #endif
1615 
1616 	res = connect ( sock, (struct sockaddr *)&uaddr, sizeof(uaddr) );
1617 	if ( res==0 )
1618 		return sock;
1619 
1620 	err = sock_errno();
1621 #ifdef EINPROGRESS
1622 	if ( err!=EWOULDBLOCK && err!=EINPROGRESS )
1623 #else
1624 	if ( err!=EWOULDBLOCK )
1625 #endif
1626 	{
1627 		set_error ( client, "connect() failed: %s", sock_error() );
1628 		return -1;
1629 	}
1630 
1631 	return sock;
1632 }
1633 #endif
1634 
1635 
net_connect_get(sphinx_client * client)1636 static int net_connect_get ( sphinx_client * client )
1637 {
1638 	struct timeval timeout;
1639 	fd_set fds_write;
1640 	int sock, to_wait, res, my_proto;
1641 
1642 	if ( client->sock>=0 )
1643 		return client->sock;
1644 
1645 	sock = -1;
1646 	if ( client->host[0]!='/' )
1647 	{
1648 		sock = net_create_inet_sock ( client );
1649 	} else
1650 	{
1651 #ifdef _WIN32
1652 		set_error ( client, "UNIX sockets are not supported on Windows" );
1653 		return -1;
1654 #else
1655 		sock = net_create_unix_sock ( client );
1656 #endif
1657 	}
1658 
1659 	if ( sock<0 )
1660 		return -1;
1661 
1662 	to_wait = (int)( 1000*client->timeout );
1663 	if ( to_wait<=0 )
1664 		to_wait = CONNECT_TIMEOUT_MSEC;
1665 
1666 	{
1667 		timeout.tv_sec = to_wait / 1000; // full seconds
1668 		timeout.tv_usec = ( to_wait % 1000 ) * 1000; // remainder is msec, so *1000 for usec
1669 		FD_ZERO ( &fds_write );
1670 		SPH_FD_SET ( sock, &fds_write );
1671 
1672 		res = select ( 1+sock, NULL, &fds_write, NULL, &timeout );
1673 
1674 		if ( res>=0 && FD_ISSET ( sock, &fds_write ) )
1675 		{
1676 			sock_set_blocking ( sock );
1677 
1678 			// now send major client protocol version
1679 			my_proto = htonl ( 1 );
1680 			if ( !net_write ( sock, (char*)&my_proto, sizeof(my_proto), client ) )
1681 			{
1682 				sock_close ( sock );
1683 				set_error ( client, "failed to send client protocol version" );
1684 				return -1;
1685 			}
1686 
1687 			// check daemon version
1688 			if ( !net_read ( sock, (char*)&my_proto, sizeof(my_proto), client ) )
1689 			{
1690 				sock_close ( sock );
1691 				return -1;
1692 			}
1693 
1694 			my_proto = ntohl ( my_proto );
1695 			if ( my_proto<1 )
1696 			{
1697 				sock_close ( sock );
1698 				set_error ( client, "expected searchd protocol version 1+, got version %d", my_proto );
1699 				return -1;
1700 			}
1701 
1702 			return sock;
1703 		}
1704 
1705 		/*!COMMIT handle EINTR here*/
1706 
1707 		sock_close ( sock );
1708 		set_error ( client, "connect() timed out" );
1709 		return -1;
1710 	}
1711 }
1712 
1713 
net_sock_eof(int sock)1714 static sphinx_bool net_sock_eof ( int sock )
1715 {
1716 	struct timeval tv;
1717 	fd_set fds_read, fds_except;
1718 	int res;
1719 	char buf;
1720 
1721 	// wrong arg, consider dead
1722 	if ( sock<0 )
1723 		return SPH_TRUE;
1724 
1725 	// select() on a socket and watch for exceptions
1726 	FD_ZERO ( &fds_read );
1727 	FD_ZERO ( &fds_except );
1728 	SPH_FD_SET ( sock, &fds_read );
1729 	SPH_FD_SET ( sock, &fds_except );
1730 	tv.tv_sec = 0;
1731 	tv.tv_usec = 0;
1732 	res = select ( 1+sock, &fds_read, NULL, &fds_except, &tv );
1733 
1734 	// select() failed, assume something is wrong
1735 	if ( res<0 )
1736 		return SPH_TRUE;
1737 
1738 	// got any events to read? (either normal via fds_read, or OOB via fds_except set)
1739 	if ( FD_ISSET ( sock, &fds_read ) || FD_ISSET ( sock, &fds_except ) )
1740 		if ( recv ( sock, &buf, sizeof(buf), MSG_PEEK )<=0 )
1741 			if ( sock_errno()!=EWOULDBLOCK )
1742 				return SPH_TRUE;
1743 
1744 	// it seems alive
1745 	return SPH_FALSE;
1746 }
1747 
1748 
net_connect_ex(sphinx_client * client)1749 static int net_connect_ex ( sphinx_client * client )
1750 {
1751 	if ( client->sock>=0 )
1752 	{
1753 		// in case of a persistent connection, check for eof
1754 		// then attempt to reestablish lost pconn once
1755 		if ( !net_sock_eof ( client->sock ) )
1756 			return client->sock;
1757 
1758 		sock_close ( client->sock );
1759 		client->sock = -1;
1760 	}
1761 
1762 	if ( !client->persist )
1763 		return net_connect_get ( client );
1764 
1765 	sphinx_open ( client );
1766 	return client->sock;
1767 }
1768 
unpack_short(char ** cur)1769 static unsigned short unpack_short ( char ** cur )
1770 {
1771 	unsigned short v;
1772 	memcpy ( &v, *cur, sizeof(unsigned short) );
1773 	(*cur) += sizeof(unsigned short);
1774 	return ntohs ( v );
1775 }
1776 
1777 
unpack_int(char ** cur)1778 static unsigned int unpack_int ( char ** cur )
1779 {
1780 	unsigned int v;
1781 	memcpy ( &v, *cur, sizeof(unsigned int) );
1782 	(*cur) += sizeof(unsigned int);
1783 	return ntohl ( v );
1784 }
1785 
1786 
unpack_str(char ** cur)1787 static char * unpack_str ( char ** cur )
1788 {
1789 	// we play a trick
1790 	// we move the string in-place to free space for trailing zero but avoid malloc
1791 	unsigned int len;
1792 	len = unpack_int ( cur );
1793 	memmove ( (*cur)-1, (*cur), len );
1794 	(*cur) += len;
1795 	(*cur)[-1] = '\0';
1796 	return (*cur)-len-1;
1797 }
1798 
1799 
unpack_qword(char ** cur)1800 static sphinx_uint64_t unpack_qword ( char ** cur )
1801 {
1802 	sphinx_uint64_t hi, lo;
1803 	hi = unpack_int ( cur );
1804 	lo = unpack_int ( cur );
1805 	return ( hi<<32 ) + lo;
1806 }
1807 
1808 
unpack_float(char ** cur)1809 static float unpack_float ( char ** cur )
1810 {
1811 	union
1812 	{
1813 		unsigned int n;
1814 		float f;
1815 	} u;
1816 	u.n = unpack_int ( cur );
1817 	return u.f;
1818 }
1819 
1820 
net_get_response(int fd,sphinx_client * client)1821 static void net_get_response ( int fd, sphinx_client * client )
1822 {
1823 	int len;
1824 	char header_buf[32], *cur, *response;
1825 	unsigned short status, ver;
1826 
1827 	// dismiss previous response
1828 	if ( client->response_buf )
1829 	{
1830 		free ( client->response_buf );
1831 		client->response_len = 0;
1832 		client->response_buf = NULL;
1833 	}
1834 
1835 	// read and parse the header
1836 	if ( !net_read ( fd, header_buf, 8, client ) )
1837 	{
1838 		sock_close ( fd );
1839 		if ( client->sock>0 )
1840 			client->sock = -1;
1841 		return;
1842 	}
1843 
1844 	cur = header_buf;
1845 	status = unpack_short ( &cur );
1846 	ver = unpack_short ( &cur );
1847 	len = unpack_int ( &cur );
1848 
1849 	// sanity check the length, alloc the buffer
1850 	if ( len<0 || len>MAX_PACKET_LEN )
1851 	{
1852 		sock_close ( fd );
1853 		if ( client->sock>0 )
1854 			client->sock = -1;
1855 		set_error ( client, "response length out of bounds (len=%d)", len );
1856 		return;
1857 	}
1858 
1859 	response = malloc ( len );
1860 	if ( !response )
1861 	{
1862 		sock_close ( fd );
1863 		if ( client->sock>0 )
1864 			client->sock = -1;
1865 		set_error ( client, "malloc() failed (bytes=%d)", len );
1866 		return;
1867 	}
1868 
1869 	// read the response
1870 	if ( !net_read ( fd, response, len, client ) )
1871 	{
1872 		sock_close ( fd );
1873 		if ( client->sock>0 )
1874 			client->sock = -1;
1875 		free ( response );
1876 		return;
1877 	}
1878 
1879 	// check status
1880 	cur = response;
1881 	switch ( status )
1882 	{
1883 		case SEARCHD_OK:
1884 		case SEARCHD_WARNING:
1885 			client->error = NULL; // so far so good
1886 			if ( status==SEARCHD_WARNING )
1887 				client->warning = unpack_str ( &cur );
1888 			else
1889 				client->warning = NULL;
1890 			client->response_len = len;
1891 			client->response_buf = response;
1892 			client->response_start = cur;
1893 			break;
1894 
1895 		case SEARCHD_ERROR:
1896 		case SEARCHD_RETRY:
1897 			// copy error message, so that we can immediately free the response
1898 			set_error ( client, "%s", unpack_str ( &cur ) );
1899 			free ( response );
1900 			break;
1901 
1902 		default:
1903 			set_error ( client, "unknown status code (status=%d)", status );
1904 			free ( response );
1905 			break;
1906 	}
1907 
1908 	// close one-time socket on success
1909 	if ( client->sock<0 )
1910 		sock_close ( fd );
1911 }
1912 
1913 
sphinx_open(sphinx_client * client)1914 sphinx_bool sphinx_open ( sphinx_client * client )
1915 {
1916 	char buf[16], *pbuf;
1917 
1918 	if ( client->sock>=0 )
1919 	{
1920 		set_error ( client, "already connected" );
1921 		return SPH_FALSE;
1922 	}
1923 
1924 	client->sock = net_connect_get ( client );
1925 	if ( client->sock<0 )
1926 		return SPH_FALSE;
1927 
1928 	pbuf = buf;
1929 	send_word ( &pbuf, SEARCHD_COMMAND_PERSIST );
1930 	send_word ( &pbuf, 0 ); // dummy version
1931 	send_int ( &pbuf, 4 ); // dummy body len
1932 	send_int ( &pbuf, 1 ); // dummy body
1933 	if ( !net_write ( client->sock, buf, (int)(pbuf-buf), client ) )
1934 	{
1935 		sock_close ( client->sock );
1936 		client->sock = -1;
1937 		return SPH_FALSE;
1938 	}
1939 	client->persist = SPH_TRUE;
1940 	return SPH_TRUE;
1941 }
1942 
1943 
sphinx_close(sphinx_client * client)1944 sphinx_bool sphinx_close ( sphinx_client * client )
1945 {
1946 	if ( client->sock<0 )
1947 	{
1948 		set_error ( client, "not connected" );
1949 		return SPH_FALSE;
1950 	}
1951 
1952 	sock_close ( client->sock );
1953 	client->sock = -1;
1954 	client->persist = SPH_FALSE;
1955 	return SPH_TRUE;
1956 }
1957 
1958 
sphinx_malloc(int len,sphinx_client * client)1959 static void * sphinx_malloc ( int len, sphinx_client * client )
1960 {
1961 	void * res;
1962 
1963 	if ( len<0 || len>MAX_PACKET_LEN )
1964 	{
1965 		set_error ( client, "malloc() length out of bounds, possibly broken response packet (len=%d)", len );
1966 		return NULL;
1967 	}
1968 
1969 	res = malloc ( len );
1970 	if ( !res )
1971 		set_error ( client, "malloc() failed (bytes=%d)", len );
1972 
1973 	return res;
1974 }
1975 
1976 
sphinx_run_queries(sphinx_client * client)1977 sphinx_result * sphinx_run_queries ( sphinx_client * client )
1978 {
1979 	int i, j, k, l, fd, len, nreqs, id64;
1980 	char req_header[32], *req, *p, *pmax;
1981 	sphinx_result * res;
1982 	union un_attr_value * pval;
1983 
1984 	if ( !client )
1985 		return NULL;
1986 
1987 	if ( client->num_reqs<=0 || client->num_reqs>MAX_REQS )
1988 	{
1989 		set_error ( client, "num_reqs=%d out of bounds (too many queries?)", client->num_reqs );
1990 		return NULL;
1991 	}
1992 
1993 	fd = net_connect_ex ( client );
1994 	if ( fd<0 )
1995 		return NULL;
1996 
1997 	// free previous results
1998 	sphinx_free_results ( client );
1999 
2000 	// send query, get response
2001 	len = 8;
2002 	for ( i=0; i<client->num_reqs; i++ )
2003 		len += client->req_lens[i];
2004 
2005 	req = req_header;
2006 	send_word ( &req, SEARCHD_COMMAND_SEARCH );
2007 	send_word ( &req, client->ver_search );
2008 	send_int ( &req, len );
2009 	send_int ( &req, 0 ); // its a client
2010 	send_int ( &req, client->num_reqs );
2011 
2012 	if ( !net_write ( fd, req_header, (int)(req-req_header), client ) )
2013 		return NULL;
2014 
2015 	for ( i=0; i<client->num_reqs; i++ )
2016 		if ( !net_write ( fd, client->reqs[i], client->req_lens[i], client ) )
2017 			return NULL;
2018 
2019 	net_get_response ( fd, client );
2020 	if ( !client->response_buf )
2021 		return NULL;
2022 
2023 	// dismiss request data, memorize count
2024 	nreqs = sphinx_dismiss_requests ( client );
2025 
2026 	// parse response
2027 	p = client->response_start;
2028 	pmax = client->response_start + client->response_len; // max position for checks, to protect against broken responses
2029 
2030 	for ( i=0; i<nreqs && p<pmax; i++ )
2031 	{
2032 		res = &client->results[i];
2033 		client->num_results++;
2034 		res->error = NULL;
2035 		res->warning = NULL;
2036 
2037 		res->status = unpack_int ( &p );
2038 		if ( res->status!=SEARCHD_OK )
2039 		{
2040 			if ( res->status==SEARCHD_WARNING )
2041 			{
2042 				res->warning = unpack_str ( &p );
2043 			} else
2044 			{
2045 				res->error = unpack_str ( &p );
2046 				continue;
2047 			}
2048 		}
2049 
2050 		// fields
2051 		res->num_fields = unpack_int ( &p );
2052 		res->fields = sphinx_malloc ( res->num_fields*sizeof(const char*), client );
2053 		if ( !res->fields )
2054 			return NULL;
2055 
2056 		for ( j=0; j<res->num_fields; j++ )
2057 			res->fields[j] = unpack_str ( &p );
2058 
2059 		// attrs
2060 		res->num_attrs = unpack_int ( &p );
2061 		res->attr_names = sphinx_malloc ( res->num_attrs*sizeof(const char*), client );
2062 		if ( !res->attr_names )
2063 			return NULL;
2064 
2065 		res->attr_types = sphinx_malloc ( res->num_attrs*sizeof(int), client );
2066 		if ( !res->attr_types )
2067 			return NULL;
2068 
2069 		for ( j=0; j<res->num_attrs; j++ )
2070 		{
2071 			res->attr_names[j] = unpack_str ( &p );
2072 			res->attr_types[j] = unpack_int ( &p );
2073 		}
2074 
2075 		// match count, id bits flag
2076 		res->num_matches = unpack_int ( &p );
2077 		id64 = unpack_int ( &p );
2078 
2079 		res->values_pool = sphinx_malloc ( (2+res->num_attrs) * res->num_matches * sizeof(union un_attr_value), client );
2080 		if ( !res->values_pool )
2081 			return NULL;
2082 		pval = res->values_pool;
2083 
2084 		// matches
2085 		for ( j=0; j<res->num_matches; j++ )
2086 		{
2087 			// id
2088 			if ( id64 )
2089 				pval->int_value = unpack_qword ( &p );
2090 			else
2091 				pval->int_value = unpack_int ( &p );
2092 			pval++;
2093 
2094 			// weight
2095 			pval->int_value = unpack_int ( &p );
2096 			pval++;
2097 
2098 			// attrs
2099 			for ( k=0; k<res->num_attrs; k++ )
2100 			{
2101 				switch ( res->attr_types[k] )
2102 				{
2103 					case SPH_ATTR_MULTI64:
2104 					case SPH_ATTR_MULTI:
2105 						/*!COMMIT this is totally unsafe on some arches (eg. SPARC)*/
2106 						pval->mva_value = (unsigned int *) p;
2107 						len = unpack_int ( &p );
2108 						for ( l=0; l<=len; l++ ) // including first one that is len
2109 							pval->mva_value[l] = ntohl ( pval->mva_value[l] );
2110 
2111 						if ( res->attr_types[k]==SPH_ATTR_MULTI64 )
2112 						{
2113 							pval->mva_value[0] = pval->mva_value[0]/2;
2114 						}
2115 						p += len*sizeof(unsigned int);
2116 						break;
2117 
2118 					case SPH_ATTR_FLOAT:	pval->float_value = unpack_float ( &p ); break;
2119 					case SPH_ATTR_BIGINT:	pval->int_value = unpack_qword ( &p ); break;
2120 					case SPH_ATTR_STRING:	pval->string = unpack_str ( &p ); break;
2121 					case SPH_ATTR_FACTORS:
2122 						len = unpack_int ( &p );
2123 						if ( len )
2124 							p += len-sizeof(unsigned int);
2125 						break;
2126 					default:				pval->int_value = unpack_int ( &p ); break;
2127 				}
2128 				pval++;
2129 			}
2130 		}
2131 
2132 		// totals
2133 		res->total = unpack_int ( &p );
2134 		res->total_found = unpack_int ( &p );
2135 		res->time_msec = unpack_int ( &p );
2136 		res->num_words = unpack_int ( &p );
2137 
2138 		if ( res->words )
2139 			free ( res->words );
2140 		res->words = sphinx_malloc ( res->num_words*sizeof(struct st_sphinx_wordinfo), client );
2141 		if ( !res->words )
2142 			return NULL;
2143 
2144 		// words
2145 		for ( j=0; j<res->num_words; j++ )
2146 		{
2147 			res->words[j].word = unpack_str ( &p );
2148 			res->words[j].docs = unpack_int ( &p );
2149 			res->words[j].hits = unpack_int ( &p );
2150 		}
2151 
2152 		// sanity check
2153 		// FIXME! add it to each unpack?
2154 		if ( p>pmax )
2155 		{
2156 			set_error ( client, "unpack error (req=%d, reqs=%d)", i, nreqs );
2157 			return NULL;
2158 		}
2159 	}
2160 
2161 	return client->results;
2162 }
2163 
2164 //////////////////////////////////////////////////////////////////////////
2165 
sphinx_get_num_results(sphinx_client * client)2166 int sphinx_get_num_results ( sphinx_client * client )
2167 {
2168 	return client ? client->num_results : -1;
2169 }
2170 
2171 
sphinx_get_id(sphinx_result * result,int match)2172 sphinx_uint64_t sphinx_get_id ( sphinx_result * result, int match )
2173 {
2174 	return sphinx_get_int ( result, match, -2 );
2175 }
2176 
2177 
sphinx_get_weight(sphinx_result * result,int match)2178 int sphinx_get_weight ( sphinx_result * result, int match )
2179 {
2180 	return (int)sphinx_get_int ( result, match, -1 );
2181 }
2182 
2183 
sphinx_get_int(sphinx_result * result,int match,int attr)2184 sphinx_int64_t sphinx_get_int ( sphinx_result * result, int match, int attr )
2185 {
2186 	// FIXME! add safety and type checks
2187 	union un_attr_value * pval;
2188 	pval = result->values_pool;
2189 	return pval [ (2+result->num_attrs)*match+2+attr ].int_value;
2190 }
2191 
2192 
sphinx_get_float(sphinx_result * result,int match,int attr)2193 float sphinx_get_float ( sphinx_result * result, int match, int attr )
2194 {
2195 	// FIXME! add safety and type checks
2196 	union un_attr_value * pval;
2197 	pval = result->values_pool;
2198 	return pval [ (2+result->num_attrs)*match+2+attr ].float_value;
2199 }
2200 
2201 
sphinx_get_mva(sphinx_result * result,int match,int attr)2202 unsigned int * sphinx_get_mva ( sphinx_result * result, int match, int attr )
2203 {
2204 	// FIXME! add safety and type checks
2205 	union un_attr_value * pval;
2206 	pval = result->values_pool;
2207 	return pval [ (2+result->num_attrs)*match+2+attr ].mva_value;
2208 }
2209 
sphinx_get_mva64_value(unsigned int * mva,int i)2210 sphinx_uint64_t sphinx_get_mva64_value ( unsigned int * mva, int i )
2211 {
2212 	sphinx_uint64_t uVal;
2213 	uVal = ( ( ( (sphinx_uint64_t)( mva[i*2] ) )<<32 ) | (sphinx_uint64_t)( mva[i*2+1] ) );
2214 	return uVal;
2215 }
2216 
sphinx_get_string(sphinx_result * result,int match,int attr)2217 const char * sphinx_get_string ( sphinx_result * result, int match, int attr )
2218 {
2219 	// FIXME! add safety and type checks
2220 	union un_attr_value * pval;
2221 	pval = result->values_pool;
2222 	return pval [ (2+result->num_attrs)*match+2+attr ].string;
2223 }
2224 
2225 //////////////////////////////////////////////////////////////////////////
2226 
net_simple_query(sphinx_client * client,char * buf,int req_len)2227 static sphinx_bool net_simple_query ( sphinx_client * client, char * buf, int req_len )
2228 {
2229 	int fd;
2230 
2231 	fd = net_connect_ex ( client );
2232 	if ( fd<0 )
2233 	{
2234 		free ( buf );
2235 		return SPH_FALSE;
2236 	}
2237 
2238 	if ( !net_write ( fd, buf, 8+req_len, client ) )
2239 	{
2240 		free ( buf );
2241 		return SPH_FALSE;
2242 	}
2243 	free ( buf );
2244 
2245 	net_get_response ( fd, client );
2246 	if ( !client->response_buf )
2247 		return SPH_FALSE;
2248 
2249 	return SPH_TRUE;
2250 }
2251 
2252 
sphinx_init_excerpt_options(sphinx_excerpt_options * opts)2253 void sphinx_init_excerpt_options ( sphinx_excerpt_options * opts )
2254 {
2255 	if ( !opts )
2256 		return;
2257 
2258 	opts->before_match		= "<b>";
2259 	opts->after_match		= "</b>";
2260 	opts->chunk_separator	= " ... ";
2261 	opts->html_strip_mode	= "index";
2262 	opts->passage_boundary	= "none";
2263 
2264 	opts->limit				= 256;
2265 	opts->limit_passages	= 0;
2266 	opts->limit_words		= 0;
2267 	opts->around			= 5;
2268 	opts->start_passage_id	= 1;
2269 
2270 	opts->exact_phrase		= SPH_FALSE;
2271 	opts->single_passage	= SPH_FALSE;
2272 	opts->use_boundaries	= SPH_FALSE;
2273 	opts->weight_order		= SPH_FALSE;
2274 	opts->query_mode		= SPH_FALSE;
2275 	opts->force_all_words	= SPH_FALSE;
2276 	opts->load_files		= SPH_FALSE;
2277 	opts->allow_empty		= SPH_FALSE;
2278 	opts->emit_zones		= SPH_FALSE;
2279 }
2280 
2281 
sphinx_build_excerpts(sphinx_client * client,int num_docs,const char ** docs,const char * index,const char * words,sphinx_excerpt_options * opts)2282 char ** sphinx_build_excerpts ( sphinx_client * client, int num_docs, const char ** docs, const char * index, const char * words, sphinx_excerpt_options * opts )
2283 {
2284 	sphinx_excerpt_options def_opt;
2285 	int i, req_len, flags;
2286 	char *buf, *req, *p, *pmax, **result;
2287 
2288 	if ( !client || !docs || !index || !words || num_docs<=0 )
2289 	{
2290 		if ( !docs )			set_error ( client, "invalid arguments (docs must not be empty)" );
2291 		else if ( !index )		set_error ( client, "invalid arguments (index must not be empty)" );
2292 		else if ( !words )		set_error ( client, "invalid arguments (words must not be empty)" );
2293 		else if ( num_docs<=0 )	set_error ( client, "invalid arguments (num_docs must be positive)" );
2294 		return NULL;
2295 	}
2296 
2297 	// fixup options
2298 	if ( !opts )
2299 	{
2300 		sphinx_init_excerpt_options ( &def_opt );
2301 		opts = &def_opt;
2302 	}
2303 
2304 	// alloc buffer
2305 	req_len = (int)( 60
2306 		+ strlen(index)
2307 		+ strlen(words)
2308 		+ safestrlen(opts->before_match)
2309 		+ safestrlen(opts->after_match)
2310 		+ safestrlen(opts->chunk_separator)
2311 		+ safestrlen(opts->html_strip_mode)
2312 		+ safestrlen(opts->passage_boundary) );
2313 	for ( i=0; i<num_docs; i++ )
2314 		req_len += (int)( 4 + safestrlen(docs[i]) );
2315 
2316 	buf = malloc ( 12+req_len ); // request body length plus 12 header bytes
2317 	if ( !buf )
2318 	{
2319 		set_error ( client, "malloc() failed (bytes=%d)", req_len );
2320 		return NULL;
2321 	}
2322 
2323 	// build request
2324 	req = buf;
2325 
2326 	send_word ( &req, SEARCHD_COMMAND_EXCERPT );
2327 	send_word ( &req, VER_COMMAND_EXCERPT );
2328 	send_int ( &req, req_len );
2329 
2330 	flags = 1; // remove spaces
2331 	if ( opts->exact_phrase )		flags |= 2;
2332 	if ( opts->single_passage )		flags |= 4;
2333 	if ( opts->use_boundaries )		flags |= 8;
2334 	if ( opts->weight_order )		flags |= 16;
2335 	if ( opts->query_mode )			flags |= 32;
2336 	if ( opts->force_all_words )	flags |= 64;
2337 	if ( opts->load_files )			flags |= 128;
2338 	if ( opts->allow_empty )		flags |= 256;
2339 	if ( opts->emit_zones )			flags |= 512;
2340 
2341 	send_int ( &req, 0 );
2342 	send_int ( &req, flags );
2343 	send_str ( &req, index );
2344 	send_str ( &req, words );
2345 
2346 	send_str ( &req, opts->before_match );
2347 	send_str ( &req, opts->after_match );
2348 	send_str ( &req, opts->chunk_separator );
2349 	send_int ( &req, opts->limit );
2350 	send_int ( &req, opts->around );
2351 
2352 	send_int ( &req, opts->limit_passages ); // v1.2
2353 	send_int ( &req, opts->limit_words );
2354 	send_int ( &req, opts->start_passage_id );
2355 	send_str ( &req, opts->html_strip_mode );
2356 	send_str ( &req, opts->passage_boundary );
2357 
2358 	send_int ( &req, num_docs );
2359 	for ( i=0; i<num_docs; i++ )
2360 		send_str ( &req, docs[i] );
2361 
2362 	if ( (int)(req-buf)!=8+req_len )
2363 	{
2364 		set_error ( client, "internal error: failed to build request in sphinx_build_excerpts()" );
2365 		free ( buf );
2366 		return NULL;
2367 	}
2368 
2369 	// send query, get response
2370 	if ( !net_simple_query ( client, buf, req_len ) )
2371 		return NULL;
2372 
2373 	// parse response
2374 	p = client->response_start;
2375 	pmax = client->response_start + client->response_len; // max position for checks, to protect against broken responses
2376 
2377 	result = malloc ( (1+num_docs)*sizeof(char*) );
2378 	if ( !result )
2379 	{
2380 		set_error ( client, "malloc() failed (bytes=%d)", (1+num_docs)*sizeof(char*) );
2381 		return NULL;
2382 	}
2383 
2384 	for ( i=0; i<=num_docs; i++ )
2385 		result[i] = NULL;
2386 
2387 	for ( i=0; i<num_docs && p<pmax; i++ )
2388 		result[i] = strdup ( unpack_str ( &p ) );
2389 
2390 	if ( p>pmax )
2391 	{
2392 		for ( i=0; i<num_docs; i++ )
2393 			if ( result[i] )
2394 				free ( result[i] );
2395 
2396 		set_error ( client, "unpack error" );
2397 		return NULL;
2398 	}
2399 
2400 	// done
2401 	return result;
2402 }
2403 
2404 //////////////////////////////////////////////////////////////////////////
2405 
sphinx_update_attributes(sphinx_client * client,const char * index,int num_attrs,const char ** attrs,int num_docs,const sphinx_uint64_t * docids,const sphinx_int64_t * values)2406 int sphinx_update_attributes ( sphinx_client * client, const char * index, int num_attrs, const char ** attrs, int num_docs, const sphinx_uint64_t * docids, const sphinx_int64_t * values )
2407 {
2408 	int i, j, req_len;
2409 	char *buf, *req, *p;
2410 
2411 	// check args
2412 	if ( !client || num_attrs<=0 || !attrs || num_docs<=0 || !docids || !values )
2413 	{
2414 		if ( num_attrs<=0 )		set_error ( client, "invalid arguments (num_attrs must be positive)" );
2415 		else if ( !index )		set_error ( client, "invalid arguments (index must not be empty)" );
2416 		else if ( !attrs )		set_error ( client, "invalid arguments (attrs must not empty)" );
2417 		else if ( num_docs<=0 )	set_error ( client, "invalid arguments (num_docs must be positive)" );
2418 		else if ( !docids )		set_error ( client, "invalid arguments (docids must not be empty)" );
2419 		else if ( !values )		set_error ( client, "invalid arguments (values must not be empty)" );
2420 	}
2421 
2422 	// alloc buffer
2423 	req_len = (int)( 12 + safestrlen(index) + (12+4*num_attrs)*num_docs );
2424 	for ( i=0; i<num_attrs; i++ )
2425 		req_len += (int)( 4 + safestrlen(attrs[i]) );
2426 
2427 	buf = malloc ( 12+req_len ); // request body length plus 12 header bytes
2428 	if ( !buf )
2429 	{
2430 		set_error ( client, "malloc() failed (bytes=%d)", req_len );
2431 		return -1;
2432 	}
2433 
2434 	// build request
2435 	req = buf;
2436 
2437 	send_word ( &req, SEARCHD_COMMAND_UPDATE );
2438 	send_word ( &req, VER_COMMAND_UPDATE );
2439 	send_int ( &req, req_len );
2440 
2441 	send_str ( &req, index );
2442 	send_int ( &req, num_attrs );
2443 	for ( i=0; i<num_attrs; i++ )
2444 	{
2445 		send_str ( &req, attrs[i] );
2446 		send_int ( &req, 0 ); // not SPH_ATTR_MULTI flag
2447 	}
2448 
2449 	send_int ( &req, num_docs );
2450 	for ( i=0; i<num_docs; i++ )
2451 	{
2452 		send_qword ( &req, docids[i] );
2453 		for ( j=0; j<num_attrs; j++ )
2454 			send_int ( &req, (unsigned int)( *values++ ) );
2455 	}
2456 
2457 	// send query, get response
2458 	if ( !net_simple_query ( client, buf, req_len ) )
2459 		return -1;
2460 
2461 	// parse response
2462 	if ( client->response_len<4 )
2463 	{
2464 		set_error ( client, "incomplete reply" );
2465 		return -1;
2466 	}
2467 
2468 	p = client->response_start;
2469 	return unpack_int ( &p );
2470 }
2471 
sphinx_update_attributes_mva(sphinx_client * client,const char * index,const char * attr,sphinx_uint64_t docid,int num_values,const unsigned int * values)2472 int sphinx_update_attributes_mva	( sphinx_client * client, const char * index, const char * attr, sphinx_uint64_t docid, int num_values, const unsigned int * values )
2473 {
2474 	int i, req_len;
2475 	char *buf, *req, *p;
2476 
2477 	// check args
2478 	if ( !client || !index || !attr || num_values<=0 || !values )
2479 	{
2480 		if ( !index )			set_error ( client, "invalid arguments (index must not be empty)" );
2481 		else if ( !attr )		set_error ( client, "invalid arguments (attr must not empty)" );
2482 		else if ( num_values<=0 )	set_error ( client, "invalid arguments (num_values must be positive)" );
2483 		else if ( !values )		set_error ( client, "invalid arguments (values must not be empty)" );
2484 	}
2485 
2486 	// alloc buffer
2487 	req_len = (int)( 38 + safestrlen(index) + safestrlen(attr) + num_values*4 );
2488 
2489 	buf = malloc ( 12+req_len ); // request body length plus 12 header bytes
2490 	if ( !buf )
2491 	{
2492 		set_error ( client, "malloc() failed (bytes=%d)", req_len );
2493 		return -1;
2494 	}
2495 
2496 	// build request
2497 	req = buf;
2498 
2499 	send_word	( &req, SEARCHD_COMMAND_UPDATE );
2500 	send_word	( &req, VER_COMMAND_UPDATE );
2501 	send_int	( &req, req_len );
2502 
2503 	send_str	( &req, index );
2504 	send_int	( &req, 1 );
2505 	send_str	( &req, attr );
2506 	send_int	( &req, 1 ); // SPH_ATTR_MULTI flag
2507 
2508 	send_int	( &req, 1 );
2509 	send_qword	( &req, docid );
2510 	send_int	( &req, num_values );
2511 	for ( i=0; i<num_values; i++ )
2512 		send_int ( &req, values[i] );
2513 
2514 	// send query, get response
2515 	if ( !net_simple_query ( client, buf, req_len ) )
2516 		return -1;
2517 
2518 	// parse response
2519 	if ( client->response_len<4 )
2520 	{
2521 		set_error ( client, "incomplete reply" );
2522 		return -1;
2523 	}
2524 
2525 	p = client->response_start;
2526 	return unpack_int ( &p );
2527 }
2528 
2529 //////////////////////////////////////////////////////////////////////////
2530 
sphinx_build_keywords(sphinx_client * client,const char * query,const char * index,sphinx_bool hits,int * out_num_keywords)2531 sphinx_keyword_info * sphinx_build_keywords ( sphinx_client * client, const char * query, const char * index, sphinx_bool hits, int * out_num_keywords )
2532 {
2533 	int i, req_len, nwords, len;
2534 	char *buf, *req, *p, *pmax;
2535 	sphinx_keyword_info *res;
2536 
2537 	// check args
2538 	if ( !client || !query || !index )
2539 	{
2540 		if ( !query )					set_error ( client, "invalid arguments (query must not be empty)" );
2541 		else if ( !index )				set_error ( client, "invalid arguments (index must not be empty)" );
2542 		else if ( !out_num_keywords )	set_error ( client, "invalid arguments (out_num_keywords must not be null)" );
2543 		return NULL;
2544 	}
2545 
2546 	// alloc buffer
2547 	req_len = (int)( safestrlen(query) + safestrlen(index) + 12 );
2548 
2549 	buf = malloc ( 12+req_len ); // request body length plus 12 header bytes
2550 	if ( !buf )
2551 	{
2552 		set_error ( client, "malloc() failed (bytes=%d)", req_len );
2553 		return NULL;
2554 	}
2555 
2556 	// build request
2557 	req = buf;
2558 
2559 	send_word ( &req, SEARCHD_COMMAND_KEYWORDS );
2560 	send_word ( &req, VER_COMMAND_KEYWORDS );
2561 	send_int ( &req, req_len );
2562 
2563 	send_str ( &req, query );
2564 	send_str ( &req, index );
2565 	send_int ( &req, hits );
2566 
2567 	// send query, get response
2568 	if ( !net_simple_query ( client, buf, req_len ) )
2569 		return NULL;
2570 
2571 	// parse response
2572 	p = client->response_start;
2573 	pmax = client->response_start + client->response_len; // max position for checks, to protect against broken responses
2574 
2575 	nwords = unpack_int ( &p );
2576 	*out_num_keywords = nwords;
2577 
2578 	len = nwords*sizeof(sphinx_keyword_info);
2579 	res = (sphinx_keyword_info*) malloc ( len );
2580 	if ( !res )
2581 	{
2582 		set_error ( client, "malloc() failed (bytes=%d)", len );
2583 		return NULL;
2584 	}
2585 	memset ( res, 0, len );
2586 
2587 	for ( i=0; i<nwords && p<pmax; i++ )
2588 	{
2589 		res[i].tokenized = strdup ( unpack_str ( &p ) );
2590 		res[i].normalized = strdup ( unpack_str ( &p ) );
2591 		if ( hits )
2592 		{
2593 			res[i].num_docs = unpack_int ( &p );
2594 			res[i].num_hits = unpack_int ( &p );
2595 		}
2596 	}
2597 	// FIXME! add check for incomplete reply
2598 
2599 	return res;
2600 }
2601 
2602 //////////////////////////////////////////////////////////////////////////
sphinx_status(sphinx_client * client,int * num_rows,int * num_cols)2603 char ** sphinx_status ( sphinx_client * client, int * num_rows, int * num_cols )
2604 {
2605 	return sphinx_status_extended ( client, num_rows, num_cols, 0 );
2606 }
2607 
sphinx_status_extended(sphinx_client * client,int * num_rows,int * num_cols,int local)2608 char ** sphinx_status_extended ( sphinx_client * client, int * num_rows, int * num_cols, int local )
2609 {
2610 	int i, j, k, n;
2611 	char *p, *pmax, *req, *buf, **res;
2612 
2613 	// check args
2614 	if ( !client || !num_rows || !num_cols )
2615 	{
2616 		if ( !num_rows )		set_error ( client, "invalid arguments (num_rows must not be NULL)" );
2617 		else if ( !num_cols )	set_error ( client, "invalid arguments (num_cols must not be NULL)" );
2618 		return NULL;
2619 	}
2620 
2621 	// build request
2622 	buf = malloc ( 12 );
2623 	if ( !buf )
2624 	{
2625 		set_error ( client, "malloc() failed (bytes=12)" );
2626 		return NULL;
2627 	}
2628 
2629 	if (local)
2630 		local=0;
2631 	else
2632 		local=1;
2633 
2634 	req = buf;
2635 	send_word ( &req, SEARCHD_COMMAND_STATUS );
2636 	send_word ( &req, VER_COMMAND_STATUS );
2637 	send_int ( &req, 4 );
2638 	send_int ( &req, local );
2639 
2640 	// send query, get response
2641 	if ( !net_simple_query ( client, buf, 12 ) )
2642 		return NULL;
2643 
2644 	// parse response
2645 	p = client->response_start;
2646 	pmax = client->response_start + client->response_len; // max position for checks, to protect against broken responses
2647 
2648 	*num_rows = unpack_int ( &p );
2649 	*num_cols = unpack_int ( &p );
2650 	n = (*num_rows)*(*num_cols);
2651 
2652 	res = (char**) malloc ( n*sizeof(char*) );
2653 	for ( i=0; i<n; i++ )
2654 		res[i] = NULL;
2655 
2656 	// FIXME! error checking?
2657 	k = 0;
2658 	for ( i=0; i<*num_rows; i++ )
2659 		for ( j=0; j<*num_cols; j++ )
2660 			res[k++] = strdup ( unpack_str ( &p ) );
2661 
2662 	return res;
2663 }
2664 
sphinx_status_destroy(char ** status,int num_rows,int num_cols)2665 void sphinx_status_destroy ( char ** status, int num_rows, int num_cols )
2666 {
2667 	int i;
2668 	for ( i=0; i<num_rows*num_cols; i++ )
2669 		free ( status[i] );
2670 	free ( status );
2671 }
2672 
2673 //
2674 // $Id$
2675 //
2676