sphinxclient.c 66 KB

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