OSDN Git Service

Initial commit of senna-1.1.2-fast.
[ludiafuncs/senna-1.1.2-fast.git] / lib / query.c
1 /* Copyright(C) 2006-2007 Brazil
2
3   This library is free software; you can redistribute it and/or
4   modify it under the terms of the GNU Lesser General Public
5   License as published by the Free Software Foundation; either
6   version 2.1 of the License, or (at your option) any later version.
7
8 nnn  This library is distributed in the hope that it will be useful,
9   but WITHOUT ANY WARRANTY; without even the implied warranty of
10   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11   Lesser General Public License for more details.
12
13   You should have received a copy of the GNU Lesser General Public
14   License along with this library; if not, write to the Free Software
15   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16 */
17
18 #include "senna_in.h"
19 #include <string.h>
20 #include <stdio.h>
21 #include <ctype.h>
22 #include "snip.h"
23 #include "sym.h"
24 #include "ql.h"
25
26 /* query string parser and executor */
27
28 #define DEFAULT_WEIGHT 5
29 #define DEFAULT_DECAYSTEP 2
30 #define DEFAULT_MAX_INTERVAL 10
31 #define DEFAULT_SIMILARITY_THRESHOLD 10
32 #define DEFAULT_TERM_EXTRACT_POLICY 0
33 #define DEFAULT_WEIGHT_VECTOR_SIZE 4096
34
35 typedef sen_obj cell;
36
37 struct _sen_query {
38   char *str;
39   char *cur;
40   char *str_end;
41   sen_sel_operator default_op;
42   sen_select_optarg opt;
43   sen_sel_mode default_mode;
44   int escalation_threshold;
45   int escalation_decaystep;
46   int weight_offset;
47   sen_set *weight_set;
48   sen_encoding encoding;
49   cell *expr;
50   int max_exprs;
51   int cur_expr;
52   int max_cells;
53   int cur_cell;
54   snip_cond *snip_conds;
55   cell cell_pool[1]; /* dummy */
56 };
57
58 inline static cell *
59 cell_new(sen_query *q)
60 {
61   if (q->cur_cell <= q->max_cells) {
62     cell *c = &q->cell_pool[q->cur_cell++];
63     return c;
64   }
65   return NULL;
66 }
67
68 inline static void
69 cell_del(sen_query *q)
70 {
71   if (q->cur_cell > 0) { q->cur_cell--; }
72 }
73
74 inline static cell *
75 cons(sen_query *q, cell *car, cell *cdr)
76 {
77   cell *c;
78   if ((c = cell_new(q))) {
79     c->type = sen_ql_list;
80     c->u.l.car = car;
81     c->u.l.cdr = cdr;
82     return c;
83   } else {
84     return NIL;
85   }
86 }
87
88 inline static cell *
89 token_new(sen_query *q, const char *start, const char *end)
90 {
91   cell *c;
92   if (start >= end) { return NIL; }
93   if ((c = cell_new(q))) {
94     unsigned int len = end - start;
95     c->type = sen_ql_bulk;
96     c->u.b.value = (char *)start;
97     c->u.b.size = len;
98     q->cur_expr++;
99     return c;
100   } else {
101     return NIL;
102   }
103 }
104
105 inline static cell *
106 op_new(sen_query *q, int8_t op, int16_t weight, int8_t mode, int32_t option)
107 {
108   cell *c;
109   if ((c = cell_new(q))) {
110     c->type = sen_ql_op;
111     c->u.op.op = op;
112     c->u.op.weight = weight;
113     c->u.op.mode = mode;
114     c->u.op.option = option;
115     return c;
116   } else {
117     return NIL;
118   }
119 }
120
121 inline static void
122 skip_space(sen_query *q)
123 {
124   unsigned int len;
125   while (q->cur < q->str_end && sen_isspace(q->cur, q->encoding)) {
126     /* null check and length check */
127     if (!(len = sen_str_charlen_nonnull(q->cur, q->str_end, q->encoding))) {
128       q->cur = q->str_end;
129       break;
130     }
131     q->cur += len;
132   }
133 }
134
135 inline static cell *
136 get_phrase(sen_query *q)
137 {
138   char *start, *s, *d;
139   start = s = d = q->cur;
140   while (1) {
141     unsigned int len;
142     if (s >= q->str_end) {
143       q->cur = s;
144       break;
145     }
146     len = sen_str_charlen_nonnull(s, q->str_end, q->encoding);
147     if (len == 1) {
148       if (*s == SEN_QUERY_QUOTER) {
149         q->cur = s + 1;
150         break;
151       } else if (*s == SEN_QUERY_ESCAPE && s + 1 < q->str_end) {
152         s++;
153         len = sen_str_charlen_nonnull(s, q->str_end, q->encoding);
154       }
155     }
156     while (len--) { *d++ = *s++; }
157   }
158   return token_new(q, start, d);
159 }
160
161 inline static cell *
162 get_word(sen_query *q, int *prefixp)
163 {
164   char *start = q->cur, *end;
165   unsigned int len;
166   for (end = q->cur;; ) {
167     /* null check and length check */
168     if (!(len = sen_str_charlen_nonnull(end, q->str_end, q->encoding))) {
169       q->cur = q->str_end;
170       break;
171     }
172     if (sen_isspace(end, q->encoding) ||
173         *end == SEN_QUERY_PARENR) {
174       q->cur = end;
175       break;
176     }
177     if (*end == SEN_QUERY_PREFIX) {
178       *prefixp = 1;
179       q->cur = end + 1;
180       break;
181     }
182     end += len;
183   }
184   return token_new(q, start, end);
185 }
186
187 inline static cell *
188 get_op(sen_query *q, sen_sel_operator op, int weight)
189 {
190   char *start, *end = q->cur;
191   int mode, option;
192   switch (*end) {
193   case 'S' :
194     mode = sen_sel_similar;
195     start = ++end;
196     option = sen_atoi(start, q->str_end, (const char **)&end);
197     if (start == end) { option = DEFAULT_SIMILARITY_THRESHOLD; }
198     q->cur = end;
199     break;
200   case 'N' :
201     mode = sen_sel_near;
202     start = ++end;
203     option = sen_atoi(start, q->str_end, (const char **)&end);
204     if (start == end) { option = DEFAULT_MAX_INTERVAL; }
205     q->cur = end;
206     break;
207   case 'n' :
208     mode = sen_sel_near2;
209     start = ++end;
210     option = sen_atoi(start, q->str_end, (const char **)&end);
211     if (start == end) { option = DEFAULT_MAX_INTERVAL; }
212     q->cur = end;
213     break;
214   case 'T' :
215     mode = sen_sel_term_extract;
216     start = ++end;
217     option = sen_atoi(start, q->str_end, (const char **)&end);
218     if (start == end) { option = DEFAULT_TERM_EXTRACT_POLICY; }
219     q->cur = end;
220     break;
221   default :
222     return NIL;
223   }
224   return op_new(q, op, weight, mode, option);
225 }
226
227 static cell *get_expr(sen_query *q);
228
229 inline static cell *
230 get_token(sen_query *q)
231 {
232   cell *token = NIL;
233   sen_sel_operator op = q->default_op;
234   {
235     int weight = DEFAULT_WEIGHT, prefixp = 0, mode = -1, option = 0;
236     skip_space(q);
237     if (q->cur_expr >= q->max_exprs ||
238         q->cur_cell >= q->max_cells ||
239         q->cur >= q->str_end) { return NIL; }
240     switch (*q->cur) {
241     case '\0' :
242       return NIL;
243     case SEN_QUERY_PARENR :
244       q->cur++;
245       return NIL;
246     case SEN_QUERY_QUOTEL :
247       q->cur++;
248       token = get_phrase(q);
249       break;
250     case SEN_QUERY_PREFIX :
251       q->cur++;
252       token = get_op(q, op, weight);
253       break;
254     case SEN_QUERY_AND :
255       q->cur++;
256       token = op_new(q, sen_sel_and, weight, mode, option);
257       break;
258     case SEN_QUERY_BUT :
259       q->cur++;
260       token = op_new(q, sen_sel_but, weight, mode, option);
261       break;
262     case SEN_QUERY_ADJ_INC :
263       q->cur++;
264       if (weight < 127) { weight++; }
265       token = op_new(q, sen_sel_adjust, weight, mode, option);
266       break;
267     case SEN_QUERY_ADJ_DEC :
268       q->cur++;
269       if (weight > -128) { weight--; }
270       token = op_new(q, sen_sel_adjust, weight, mode, option);
271       break;
272     case SEN_QUERY_ADJ_NEG :
273       q->cur++;
274       token = op_new(q, sen_sel_adjust, -1, mode, option);
275       break;
276     case SEN_QUERY_PARENL :
277       q->cur++;
278       token = get_expr(q);
279       break;
280     default :
281       if ((token = get_word(q, &prefixp)) &&
282           token->u.b.value[0] == 'O' &&
283           token->u.b.value[1] == 'R' &&
284           token->u.b.size == 2) {
285         cell_del(q);
286         q->cur_expr--;
287         token = op_new(q, sen_sel_or, weight, mode, option);
288       }
289       break;
290     }
291   }
292   return cons(q, token, NIL);
293 }
294
295 static cell *
296 get_expr(sen_query *q)
297 {
298   cell *r, *c, *c_;
299   for (c = r = get_token(q); c != NIL; c = c_) {
300     c_ = c->u.l.cdr = get_token(q);
301   }
302   return r;
303 }
304
305 static const char *
306 get_weight_vector(sen_query *query, const char *source)
307 {
308   sen_ctx *ctx = &sen_gctx; /* todo : replace it with the local ctx */
309   const char *p;
310
311   if (!query->opt.weight_vector &&
312       !query->weight_set &&
313       !(query->opt.weight_vector = SEN_CALLOC(sizeof(int) * DEFAULT_WEIGHT_VECTOR_SIZE))) {
314     SEN_LOG(sen_log_alert, "get_weight_vector malloc fail");
315     return source;
316   }
317   for (p = source; p < query->str_end; ) {
318     unsigned int key;
319     int value;
320
321     /* key, key is not zero */
322     key = sen_atoui(p, query->str_end, &p);
323     if (!key || key > SEN_ID_MAX) { break; }
324
325     /* value */
326     if (*p == ':') {
327       p++;
328       value = sen_atoi(p, query->str_end, &p);
329     } else {
330       value = 1;
331     }
332
333     if (query->weight_set) {
334       int *pval;
335       if (sen_set_get(query->weight_set, &key, (void **)&pval)) {
336         *pval = value;
337       }
338     } else if (key < DEFAULT_WEIGHT_VECTOR_SIZE) {
339       query->opt.weight_vector[key - 1] = value;
340     } else {
341       SEN_FREE(query->opt.weight_vector);
342       query->opt.weight_vector = NULL;
343       if (!(query->weight_set = sen_set_open(sizeof(unsigned int), sizeof(int), 0))) {
344         return source;
345       }
346       p = source;           /* reparse */
347       continue;
348     }
349     if (*p != ',') { break; }
350     p++;
351   }
352   return p;
353 }
354
355 inline static void
356 get_pragma(sen_query *q)
357 {
358   char *start, *end = q->cur;
359   while (end < q->str_end && *end == SEN_QUERY_PREFIX) {
360     if (++end >= q->str_end) { break; }
361     switch (*end) {
362     case 'E' :
363       start = ++end;
364       q->escalation_threshold = sen_atoi(start, q->str_end, (const char **)&end);
365       while (end < q->str_end && (isdigit(*end) || *end == '-')) { end++; }
366       if (*end == ',') {
367         start = ++end;
368         q->escalation_decaystep = sen_atoi(start, q->str_end, (const char **)&end);
369       }
370       q->cur = end;
371       break;
372     case 'D' :
373       start = ++end;
374       while (end < q->str_end && *end != SEN_QUERY_PREFIX && !sen_isspace(end, q->encoding)) {
375         end++;
376       }
377       if (end > start) {
378         switch (*start) {
379         case 'O' :
380           q->default_op = sen_sel_or;
381           break;
382         case SEN_QUERY_AND :
383           q->default_op = sen_sel_and;
384           break;
385         case SEN_QUERY_BUT :
386           q->default_op = sen_sel_but;
387           break;
388         case SEN_QUERY_ADJ_INC :
389           q->default_op = sen_sel_adjust;
390           break;
391         }
392       }
393       q->cur = end;
394       break;
395     case 'W' :
396       start = ++end;
397       end = (char *)get_weight_vector(q, start);
398       q->cur = end;
399       break;
400     }
401   }
402 }
403
404 static int
405 section_weight_cb(sen_records *r, const void *rid, int sid, void *arg)
406 {
407   int *w;
408   sen_set *s = (sen_set *)arg;
409   if (s && sen_set_at(s, &sid, (void **)&w)) {
410     return *w;
411   } else {
412     return 0;
413   }
414 }
415
416 sen_query *
417 sen_query_open(const char *str, unsigned int str_len,
418                sen_sel_operator default_op, int max_exprs, sen_encoding encoding)
419 {
420   sen_ctx *ctx = &sen_gctx; /* todo : replace it with the local ctx */
421   sen_query *q;
422   int max_cells = max_exprs * 4;
423   if (!(q = SEN_MALLOC(sizeof(sen_query) + max_cells * sizeof(cell) + str_len + 1))) {
424     SEN_LOG(sen_log_alert, "sen_query_open malloc fail");
425     return NULL;
426   }
427   q->str = (char *)&q->cell_pool[max_cells];
428   memcpy(q->str, str, str_len);
429   q->str[str_len] = '\0';
430   q->cur = q->str;
431   q->str_end = q->str + str_len;
432   q->default_op = default_op;
433   q->encoding = encoding;
434   q->max_exprs = max_exprs;
435   q->max_cells = max_cells;
436   q->cur_cell = 0;
437   q->cur_expr = 0;
438   q->escalation_threshold = SENNA_DEFAULT_QUERY_ESCALATION_THRESHOLD;
439   q->escalation_decaystep = DEFAULT_DECAYSTEP;
440   q->weight_offset = 0;
441   q->opt.weight_vector = NULL;
442   q->weight_set = NULL;
443   get_pragma(q);
444   q->expr = get_expr(q);
445   q->opt.vector_size = DEFAULT_WEIGHT_VECTOR_SIZE;
446   q->opt.func = q->weight_set ? section_weight_cb : NULL;
447   q->opt.func_arg = q->weight_set;
448   q->snip_conds = NULL;
449   return q;
450 }
451
452 unsigned int
453 sen_query_rest(sen_query *q, const char ** const rest)
454 {
455   if (!q) { return 0; }
456   if (rest) {
457     *rest = q->cur;
458   }
459   return (unsigned int)(q->str_end - q->cur);
460 }
461
462 sen_rc
463 sen_query_close(sen_query *q)
464 {
465   sen_ctx *ctx = &sen_gctx; /* todo : replace it with the local ctx */
466   if (!q) { return sen_invalid_argument; }
467   if (q->opt.weight_vector) {
468     SEN_FREE(q->opt.weight_vector);
469   }
470   if (q->weight_set) {
471     sen_set_close(q->weight_set);
472   }
473   if (q->snip_conds) {
474     snip_cond *sc;
475     for (sc = q->snip_conds; sc < q->snip_conds + q->cur_expr; sc++) {
476       sen_snip_cond_close(sc);
477     }
478     SEN_FREE(q->snip_conds);
479   }
480   SEN_FREE(q);
481   return sen_success;
482 }
483
484 static void
485 exec_query(sen_index *i, sen_query *q, cell *c, sen_records *r, sen_sel_operator op)
486 {
487   sen_records *s;
488   cell *e, *ope = NIL;
489   int n = sen_records_nhits(r);
490   sen_sel_operator op0 = sen_sel_or, *opp = &op0, op1 = q->default_op;
491   if (!n && op != sen_sel_or) { return; }
492   s = n ? sen_records_open(r->record_unit, r->subrec_unit, 0) : r;
493   while (c != NIL) {
494     POP(e, c);
495     switch (e->type) {
496     case sen_ql_op :
497       if (opp == &op0 && e->u.op.op == sen_sel_but) {
498         POP(e, c);
499       } else {
500         ope = e;
501         op1 = ope->u.op.op;
502       }
503       continue;
504     case sen_ql_bulk :
505       if (ope != NIL) {
506         q->opt.mode = ope->u.op.mode == -1 ? q->default_mode : ope->u.op.mode;
507         q->opt.max_interval = q->opt.similarity_threshold = ope->u.op.option;
508         if (!q->opt.weight_vector) {
509           q->opt.vector_size = ope->u.op.weight + q->weight_offset;
510         }
511         if (ope->u.op.mode == sen_sel_similar) {
512           q->opt.max_interval = q->default_mode;
513         }
514       } else {
515         q->opt.mode = q->default_mode;
516         q->opt.max_interval = DEFAULT_MAX_INTERVAL;
517         q->opt.similarity_threshold = DEFAULT_SIMILARITY_THRESHOLD;
518         if (!q->opt.weight_vector) {
519           q->opt.vector_size = DEFAULT_WEIGHT + q->weight_offset;
520         }
521       }
522       if (sen_index_select(i, e->u.b.value, e->u.b.size, s, *opp, &q->opt)) {
523         SEN_LOG(sen_log_error, "sen_index_select on exec_query failed !");
524         return;
525       }
526       break;
527     case sen_ql_list :
528       exec_query(i, q, e, s, *opp);
529       break;
530     default :
531       SEN_LOG(sen_log_notice, "invalid object assigned in query (%d)", e->type);
532       break;
533     }
534     opp = &op1;
535     ope = NIL;
536     op1 = q->default_op;
537   }
538   if (n) {
539     switch (op) {
540     case sen_sel_or :
541       if (!sen_records_union(r, s)) { sen_records_close(s); }
542       break;
543     case sen_sel_and :
544       if (!sen_records_intersect(r, s)) { sen_records_close(s); }
545       break;
546     case sen_sel_but :
547       if (!sen_records_subtract(r, s)) { sen_records_close(s); }
548       break;
549       /* todo: adjust
550     case sen_sel_adjust :
551       break;
552       */
553     default :
554       sen_records_close(s);
555       break;
556     }
557   }
558 }
559
560 sen_rc
561 sen_query_exec(sen_index *i, sen_query *q, sen_records *r, sen_sel_operator op)
562 {
563   int p;
564   if (!i || !q || !r || !PAIRP(q->expr)) { return sen_invalid_argument; }
565   p = q->escalation_threshold;
566   // dump_query(q, q->expr, 0);
567   // sen_log("escalation_threshold=%d", p);
568   if (p >= 0 || (-p & 1)) {
569     q->default_mode = sen_sel_exact;
570     exec_query(i, q, q->expr, r, op);
571     SEN_LOG(sen_log_info, "hits(exact)=%d", sen_records_nhits(r));
572   }
573   if ((p >= 0) ? (p >= sen_records_nhits(r)) : (-p & 2)) {
574     q->weight_offset -= q->escalation_decaystep;
575     q->default_mode = sen_sel_unsplit;
576     exec_query(i, q, q->expr, r, op);
577     SEN_LOG(sen_log_info, "hits(unsplit)=%d", sen_records_nhits(r));
578   }
579   if ((p >= 0) ? (p >= sen_records_nhits(r)) : (-p & 4)) {
580     q->weight_offset -= q->escalation_decaystep;
581     q->default_mode = sen_sel_partial;
582     exec_query(i, q, q->expr, r, op);
583     SEN_LOG(sen_log_info, "hits(partial)=%d", sen_records_nhits(r));
584   }
585   return sen_success;
586 }
587
588 static int
589 query_term_rec(sen_query* q, cell* c, query_term_callback func, void *func_arg)
590 {
591   cell *token;
592   if (BULKP(c)) {
593     return func(c->u.b.value, c->u.b.size, func_arg);
594   }
595   for (token = c; PAIRP(token); token = CDR(token)) {
596     if (!query_term_rec(q, CAR(token), func, func_arg)) {
597       return 0; /* abort */
598     }
599   }
600   return 1; /* continue */
601 }
602
603 void
604 sen_query_term(sen_query *q, query_term_callback func, void *func_arg)
605 {
606   query_term_rec(q, q->expr, func, func_arg);
607 }
608
609 /* FIXME: for test */
610 sen_rc
611 sen_query_str(sen_query *q, const char **str, unsigned int *len)
612 {
613   if (str) { *str = q->str; }
614   if (len) { *len = q->str_end - q->str; }
615   return sen_success;
616 }
617
618 static void
619 scan_keyword(snip_cond *sc, sen_nstr *str, sen_id section,
620              sen_sel_operator op, sen_select_optarg *optarg,
621              int *found, int *score)
622 {
623   int tf;
624   int w = 1;
625   for (tf = 0; ; tf++) {
626     sen_bm_tunedbm(sc, str, 0);
627     if (sc->stopflag == SNIPCOND_STOP) { break; }
628   }
629   if (optarg->vector_size) {
630     if (!optarg->weight_vector) {
631       w = optarg->vector_size;
632     } else if (section) {
633       w = (section <= optarg->vector_size ?
634                       optarg->weight_vector[section - 1] : 0);
635     }
636   }
637   switch (op) {
638   case sen_sel_or :
639     if (tf) {
640       *found = 1;
641       *score += w * tf;
642     }
643     break;
644   case sen_sel_and :
645     if (tf) {
646       *score += w * tf;
647     } else {
648       *found = 0;
649     }
650     break;
651   case sen_sel_but :
652     if (tf) {
653       *found = 0;
654     }
655     break;
656   case sen_sel_adjust :
657     *score += w * tf;
658   }
659 }
660
661 /* TODO: delete overlapping logic with exec_query */
662 static sen_rc
663 scan_query(sen_query *q, sen_nstr *nstr, sen_id section, cell *c, snip_cond **sc,
664            sen_sel_operator op, int flags, int *found, int *score)
665 {
666   int _found = 0, _score = 0;
667   cell *e, *ope = NIL;
668   sen_sel_operator op0 = sen_sel_or, *opp = &op0, op1 = q->default_op;
669   while (c != NIL) {
670     POP(e, c);
671     switch (e->type) {
672     case sen_ql_op :
673       if (opp == &op0 && e->u.op.op == sen_sel_but) {
674         POP(e, c);
675       } else {
676         ope = e;
677         op1 = ope->u.op.op;
678       }
679       continue;
680     case sen_ql_bulk :
681       if (ope != NIL) {
682         q->opt.mode = ope->u.op.mode == -1 ? q->default_mode : ope->u.op.mode;
683         q->opt.max_interval = q->opt.similarity_threshold = ope->u.op.option;
684         if (!q->opt.weight_vector) {
685           q->opt.vector_size = ope->u.op.weight + q->weight_offset;
686         }
687       } else {
688         q->opt.mode = q->default_mode;
689         q->opt.max_interval = DEFAULT_MAX_INTERVAL;
690         q->opt.similarity_threshold = DEFAULT_SIMILARITY_THRESHOLD;
691         if (!q->opt.weight_vector) {
692           q->opt.vector_size = DEFAULT_WEIGHT + q->weight_offset;
693         }
694       }
695       if ((flags & SEN_QUERY_SCAN_ALLOCCONDS)) {
696         sen_rc rc;
697         /* NOTE: SEN_SNIP_NORMALIZE = SEN_QUERY_SCAN_NORMALIZE */
698         if ((rc = sen_snip_cond_init(*sc, e->u.b.value, e->u.b.size,
699                                      q->encoding, flags & SEN_SNIP_NORMALIZE))) {
700           return rc;
701         }
702       } else {
703         sen_snip_cond_reinit(*sc);
704       }
705       scan_keyword(*sc, nstr, section, *opp, &q->opt, &_found, &_score);
706       (*sc)++;
707       break;
708     case sen_ql_list :
709       scan_query(q, nstr, section, e, sc, *opp, flags, &_found, &_score);
710       break;
711     default :
712       SEN_LOG(sen_log_notice, "invalid object assigned in query! (%d)", e->type);
713       break;
714     }
715     opp = &op1;
716     ope = NIL;
717     op1 = q->default_op;
718   }
719   switch (op) {
720   case sen_sel_or :
721     *found |= _found;
722     *score += _score;
723     break;
724   case sen_sel_and :
725     *found &= _found;
726     *score += _score;
727     break;
728   case sen_sel_but :
729     *found &= !_found;
730     break;
731   case sen_sel_adjust :
732     *score += _score;
733     break;
734   default :
735     break;
736   }
737   return sen_success;
738 }
739
740 static sen_rc
741 alloc_snip_conds(sen_query *q)
742 {
743   sen_ctx *ctx = &sen_gctx; /* todo : replace it with the local ctx */
744   if (!(q->snip_conds = SEN_CALLOC(sizeof(snip_cond) * q->cur_expr))) {
745     SEN_LOG(sen_log_alert, "snip_cond allocation failed");
746     return sen_memory_exhausted;
747   }
748   return sen_success;
749 }
750
751 sen_rc
752 sen_query_scan(sen_query *q, const char **strs, unsigned int *str_lens, unsigned int nstrs,
753                int flags, int *found, int *score)
754 {
755   unsigned int i;
756   sen_rc rc;
757   if (!q || !strs || !nstrs) { return sen_invalid_argument; }
758   *found = *score = 0;
759   if (!q->snip_conds) {
760     if ((rc = alloc_snip_conds(q))) { return rc; }
761     flags |= SEN_QUERY_SCAN_ALLOCCONDS;
762   } else if (flags & SEN_QUERY_SCAN_ALLOCCONDS) {
763     SEN_LOG(sen_log_warning, "invalid flags specified on sen_query_scan")
764     return sen_invalid_argument;
765   }
766   for (i = 0; i < nstrs; i++) {
767     sen_nstr *n;
768     snip_cond *sc = q->snip_conds;
769     if (flags & SEN_QUERY_SCAN_NORMALIZE) {
770       n = sen_nstr_open(*(strs + i), *(str_lens + i), q->encoding,
771                         SEN_STR_WITH_CHECKS | SEN_STR_REMOVEBLANK);
772     } else {
773       n = sen_fakenstr_open(*(strs + i), *(str_lens + i), q->encoding,
774                         SEN_STR_WITH_CHECKS | SEN_STR_REMOVEBLANK);
775     }
776     if (!n) { return sen_memory_exhausted; }
777     if ((rc = scan_query(q, n, i + 1, q->expr, &sc, sen_sel_or, flags, found, score))) {
778       sen_nstr_close(n);
779       return rc;
780     }
781     flags &= ~SEN_QUERY_SCAN_ALLOCCONDS;
782     sen_nstr_close(n);
783   }
784   return sen_success;
785 }
786
787 /* TODO: delete overlapping logic with exec_query */
788 sen_rc
789 snip_query(sen_query *q, sen_snip *snip, cell *c, sen_sel_operator op,
790            unsigned int n_tags, int c_but,
791            const char **opentags, unsigned int *opentag_lens,
792            const char **closetags, unsigned int *closetag_lens)
793 {
794   cell *e, *ope = NIL;
795   sen_sel_operator op0 = sen_sel_or, *opp = &op0, op1 = q->default_op;
796   while (c != NIL) {
797           POP(e, c);
798     switch (e->type) {
799     case sen_ql_op :
800       ope = e;
801       op1 = ope->u.op.op;
802       continue;
803     case sen_ql_bulk :
804       if (ope != NIL) {
805         q->opt.mode = ope->u.op.mode == -1 ? q->default_mode : ope->u.op.mode;
806       } else {
807         q->opt.mode = q->default_mode;
808       }
809       if (!(c_but ^ (*opp == sen_sel_but))) {
810         sen_rc rc;
811         unsigned int i = snip->cond_len % n_tags;
812         if ((rc = sen_snip_add_cond(snip, e->u.b.value, e->u.b.size,
813                                     opentags[i], opentag_lens[i],
814                                     closetags[i], closetag_lens[i]))) {
815           return rc;
816         }
817       }
818       break;
819     case sen_ql_list :
820       snip_query(q, snip, e, *opp, n_tags, (*opp == sen_sel_but) ? c_but ^ 1 : c_but,
821                  opentags, opentag_lens, closetags, closetag_lens);
822       break;
823     default :
824       SEN_LOG(sen_log_notice, "invalid object assigned in query!! (%d)", e->type);
825       break;
826     }
827     opp = &op1;
828     ope = NIL;
829     op1 = q->default_op;
830   }
831   return sen_success;
832 }
833
834 sen_snip *
835 sen_query_snip(sen_query *query, int flags,
836                unsigned int width, unsigned int max_results,
837                unsigned int n_tags,
838                const char **opentags, unsigned int *opentag_lens,
839                const char **closetags, unsigned int *closetag_lens,
840                sen_snip_mapping *mapping)
841 {
842   sen_snip *res;
843   if (!(res = sen_snip_open(query->encoding, flags, width, max_results,
844                             NULL, 0, NULL, 0, mapping))) {
845     return NULL;
846   }
847   if (snip_query(query, res, query->expr, sen_sel_or, n_tags, 0,
848                  opentags, opentag_lens, closetags, closetag_lens)) {
849     sen_snip_close(res);
850     return NULL;
851   }
852   return res;
853 }
854
855 #ifdef DEBUG
856
857 static void
858 dump_query(sen_query *q, cell *c, int level)
859 {
860   { int i; for (i = level; i; i--) { putchar(' '); }}
861   printf("%d:%d ", c->weight, c->op);
862   if (c->type == cell_token) {
863     { int i; for (i = level; i; i--) { putchar(' '); }}
864     fwrite(c->u.b.value, 1, c->u.b.size, stdout);
865   } else {
866     cell *token;
867     putchar('\n');
868     for (token = c->u.l.car; token; token = token->u.l.cdr) {
869       dump_query(q, token, level + 1);
870     }
871   }
872 }
873
874 #endif /* DEBUG */