NetSurf
search.c
Go to the documentation of this file.
1 /*
2  * Copyright 2004 John M Bell <jmb202@ecs.soton.ac.uk>
3  * Copyright 2005 Adrian Lees <adrianl@users.sourceforge.net>
4  * Copyright 2009 Mark Benjamin <netsurf-browser.org.MarkBenjamin@dfgh.net>
5  *
6  * This file is part of NetSurf, http://www.netsurf-browser.org/
7  *
8  * NetSurf is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; version 2 of the License.
11  *
12  * NetSurf is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program. If not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 /**
22  * \file
23  * Free text search (core)
24  */
25 
26 #include <ctype.h>
27 #include <string.h>
28 #include <dom/dom.h>
29 
30 #include "utils/config.h"
31 #include "utils/log.h"
32 #include "utils/messages.h"
33 #include "utils/utils.h"
34 #include "content/content.h"
35 #include "content/hlcache.h"
36 #include "desktop/selection.h"
37 #include "netsurf/search.h"
38 #include "netsurf/misc.h"
39 #include "desktop/gui_internal.h"
40 
41 #include "text/textplain.h"
42 #include "html/box.h"
43 #include "html/box_inspect.h"
44 #include "html/html.h"
45 #include "html/private.h"
46 #include "html/search.h"
47 
48 #ifndef NOF_ELEMENTS
49 #define NOF_ELEMENTS(array) (sizeof(array)/sizeof(*(array)))
50 #endif
51 
52 
53 struct list_entry {
54  unsigned start_idx; /* start position of match */
55  unsigned end_idx; /* end of match */
56 
57  struct box *start_box; /* used only for html contents */
58  struct box *end_box;
59 
60  struct selection *sel;
61 
62  struct list_entry *prev;
63  struct list_entry *next;
64 };
65 
67  void *gui_p;
68  struct content *c;
69  struct list_entry *found;
70  struct list_entry *current; /* first for select all */
71  char *string;
73  bool newsearch;
74  bool is_html;
75 };
76 
77 
78 /* Exported function documented in search.h */
79 struct search_context *
80 search_create_context(struct content *c, content_type type, void *gui_data)
81 {
82  struct search_context *context;
83  struct list_entry *search_head;
84 
85  if (type != CONTENT_HTML && type != CONTENT_TEXTPLAIN) {
86  return NULL;
87  }
88 
89  context = malloc(sizeof(struct search_context));
90  if (context == NULL) {
91  return NULL;
92  }
93 
94  search_head = malloc(sizeof(struct list_entry));
95  if (search_head == NULL) {
96  free(context);
97  return NULL;
98  }
99 
100  search_head->start_idx = 0;
101  search_head->end_idx = 0;
102  search_head->start_box = NULL;
103  search_head->end_box = NULL;
104  search_head->sel = NULL;
105  search_head->prev = NULL;
106  search_head->next = NULL;
107 
108  context->found = search_head;
109  context->current = NULL;
110  context->string = NULL;
111  context->prev_case_sens = false;
112  context->newsearch = true;
113  context->c = c;
114  context->is_html = (type == CONTENT_HTML) ? true : false;
115  context->gui_p = gui_data;
116 
117  return context;
118 }
119 
120 
121 /**
122  * Release the memory used by the list of matches,
123  * deleting selection objects too
124  */
125 
126 static void free_matches(struct search_context *context)
127 {
128  struct list_entry *a;
129  struct list_entry *b;
130 
131  a = context->found->next;
132 
133  /* empty the list before clearing and deleting the
134  * selections because the the clearing updates the
135  * screen immediately, causing nested accesses to the list */
136 
137  context->found->prev = NULL;
138  context->found->next = NULL;
139 
140  for (; a; a = b) {
141  b = a->next;
142  if (a->sel) {
143  selection_clear(a->sel, true);
145  }
146  free(a);
147  }
148 }
149 
150 
151 /**
152  * Find the first occurrence of 'match' in 'string' and return its index
153  *
154  * \param string the string to be searched (unterminated)
155  * \param s_len length of the string to be searched
156  * \param pattern the pattern for which we are searching (unterminated)
157  * \param p_len length of pattern
158  * \param case_sens true iff case sensitive match required
159  * \param m_len accepts length of match in bytes
160  * \return pointer to first match, NULL if none
161  */
162 
163 static const char *find_pattern(const char *string, int s_len,
164  const char *pattern, int p_len, bool case_sens,
165  unsigned int *m_len)
166 {
167  struct { const char *ss, *s, *p; bool first; } context[16];
168  const char *ep = pattern + p_len;
169  const char *es = string + s_len;
170  const char *p = pattern - 1; /* a virtual '*' before the pattern */
171  const char *ss = string;
172  const char *s = string;
173  bool first = true;
174  int top = 0;
175 
176  while (p < ep) {
177  bool matches;
178  if (p < pattern || *p == '*') {
179  char ch;
180 
181  /* skip any further asterisks; one is the same as many
182  */
183  do p++; while (p < ep && *p == '*');
184 
185  /* if we're at the end of the pattern, yes, it matches
186  */
187  if (p >= ep) break;
188 
189  /* anything matches a # so continue matching from
190  here, and stack a context that will try to match
191  the wildcard against the next character */
192 
193  ch = *p;
194  if (ch != '#') {
195  /* scan forwards until we find a match for
196  this char */
197  if (!case_sens) ch = toupper(ch);
198  while (s < es) {
199  if (case_sens) {
200  if (*s == ch) break;
201  } else if (toupper(*s) == ch)
202  break;
203  s++;
204  }
205  }
206 
207  if (s < es) {
208  /* remember where we are in case the match
209  fails; we may then resume */
210  if (top < (int)NOF_ELEMENTS(context)) {
211  context[top].ss = ss;
212  context[top].s = s + 1;
213  context[top].p = p - 1;
214  /* ptr to last asterisk */
215  context[top].first = first;
216  top++;
217  }
218 
219  if (first) {
220  ss = s;
221  /* remember first non-'*' char */
222  first = false;
223  }
224 
225  matches = true;
226  } else {
227  matches = false;
228  }
229 
230  } else if (s < es) {
231  char ch = *p;
232  if (ch == '#')
233  matches = true;
234  else {
235  if (case_sens)
236  matches = (*s == ch);
237  else
238  matches = (toupper(*s) == toupper(ch));
239  }
240  if (matches && first) {
241  ss = s; /* remember first non-'*' char */
242  first = false;
243  }
244  } else {
245  matches = false;
246  }
247 
248  if (matches) {
249  p++; s++;
250  } else {
251  /* doesn't match,
252  * resume with stacked context if we have one */
253  if (--top < 0)
254  return NULL; /* no match, give up */
255 
256  ss = context[top].ss;
257  s = context[top].s;
258  p = context[top].p;
259  first = context[top].first;
260  }
261  }
262 
263  /* end of pattern reached */
264  *m_len = max(s - ss, 1);
265  return ss;
266 }
267 
268 
269 /**
270  * Add a new entry to the list of matches
271  *
272  * \param start_idx Offset of match start within textual representation
273  * \param end_idx Offset of match end
274  * \param context The search context to add the entry to.
275  * \return Pointer to added entry, NULL iff failed.
276  */
277 static struct list_entry *
278 add_entry(unsigned start_idx, unsigned end_idx, struct search_context *context)
279 {
280  struct list_entry *entry;
281 
282  /* found string in box => add to list */
283  entry = calloc(1, sizeof(*entry));
284  if (!entry) {
285  return NULL;
286  }
287 
288  entry->start_idx = start_idx;
289  entry->end_idx = end_idx;
290  entry->sel = NULL;
291 
292  entry->next = 0;
293  entry->prev = context->found->prev;
294 
295  if (context->found->prev == NULL) {
296  context->found->next = entry;
297  } else {
298  context->found->prev->next = entry;
299  }
300 
301  context->found->prev = entry;
302 
303  return entry;
304 }
305 
306 
307 /**
308  * Finds all occurrences of a given string in the html box tree
309  *
310  * \param pattern the string pattern to search for
311  * \param p_len pattern length
312  * \param cur pointer to the current box
313  * \param case_sens whether to perform a case sensitive search
314  * \param context The search context to add the entry to.
315  * \return true on success, false on memory allocation failure
316  */
317 static bool find_occurrences_html(const char *pattern, int p_len,
318  struct box *cur, bool case_sens,
319  struct search_context *context)
320 {
321  struct box *a;
322 
323  /* ignore this box, if there's no visible text */
324  if (!cur->object && cur->text) {
325  const char *text = cur->text;
326  unsigned length = cur->length;
327 
328  while (length > 0) {
329  struct list_entry *entry;
330  unsigned match_length;
331  unsigned match_offset;
332  const char *new_text;
333  const char *pos = find_pattern(text, length,
334  pattern, p_len, case_sens,
335  &match_length);
336  if (!pos)
337  break;
338 
339  /* found string in box => add to list */
340  match_offset = pos - cur->text;
341 
342  entry = add_entry(cur->byte_offset + match_offset,
343  cur->byte_offset +
344  match_offset +
345  match_length, context);
346  if (!entry)
347  return false;
348 
349  entry->start_box = cur;
350  entry->end_box = cur;
351 
352  new_text = pos + match_length;
353  length -= (new_text - text);
354  text = new_text;
355  }
356  }
357 
358  /* and recurse */
359  for (a = cur->children; a; a = a->next) {
360  if (!find_occurrences_html(pattern, p_len, a, case_sens,
361  context))
362  return false;
363  }
364 
365  return true;
366 }
367 
368 
369 /**
370  * Finds all occurrences of a given string in a textplain content
371  *
372  * \param pattern the string pattern to search for
373  * \param p_len pattern length
374  * \param c the content to be searched
375  * \param case_sens whether to perform a case sensitive search
376  * \param context The search context to add the entry to.
377  * \return true on success, false on memory allocation failure
378  */
379 
380 static bool find_occurrences_text(const char *pattern, int p_len,
381  struct content *c, bool case_sens,
382  struct search_context *context)
383 {
384  int nlines = textplain_line_count(c);
385  int line;
386 
387  for(line = 0; line < nlines; line++) {
388  size_t offset, length;
389  const char *text = textplain_get_line(c, line,
390  &offset, &length);
391  if (text) {
392  while (length > 0) {
393  struct list_entry *entry;
394  unsigned match_length;
395  size_t start_idx;
396  const char *new_text;
397  const char *pos = find_pattern(text, length,
398  pattern, p_len, case_sens,
399  &match_length);
400  if (!pos)
401  break;
402 
403  /* found string in line => add to list */
404  start_idx = offset + (pos - text);
405  entry = add_entry(start_idx, start_idx +
406  match_length, context);
407  if (!entry)
408  return false;
409 
410  new_text = pos + match_length;
411  offset += (new_text - text);
412  length -= (new_text - text);
413  text = new_text;
414  }
415  }
416  }
417 
418  return true;
419 }
420 
421 
422 /**
423  * Specifies whether all matches or just the current match should
424  * be highlighted in the search text.
425  */
426 static void search_show_all(bool all, struct search_context *context)
427 {
428  struct list_entry *a;
429 
430  for (a = context->found->next; a; a = a->next) {
431  bool add = true;
432  if (!all && a != context->current) {
433  add = false;
434  if (a->sel) {
435  selection_clear(a->sel, true);
437  a->sel = NULL;
438  }
439  }
440  if (add && !a->sel) {
441 
442  if (context->is_html == true) {
443  html_content *html = (html_content *)context->c;
444  a->sel = selection_create(context->c, true);
445  if (!a->sel)
446  continue;
447 
448  selection_init(a->sel, html->layout,
449  &html->len_ctx);
450  } else {
451  a->sel = selection_create(context->c, false);
452  if (!a->sel)
453  continue;
454 
455  selection_init(a->sel, NULL, NULL);
456  }
457 
459  selection_set_end(a->sel, a->end_idx);
460  }
461  }
462 }
463 
464 
465 /**
466  * Search for a string in the box tree
467  *
468  * \param string the string to search for
469  * \param string_len length of search string
470  * \param context The search context to add the entry to.
471  * \param flags flags to control the search.
472  */
473 static void
474 search_text(const char *string,
475  int string_len,
476  struct search_context *context,
477  search_flags_t flags)
478 {
479  struct rect bounds;
480  struct box *box = NULL;
481  union content_msg_data msg_data;
482  bool case_sensitive, forwards, showall;
483 
484  case_sensitive = ((flags & SEARCH_FLAG_CASE_SENSITIVE) != 0) ?
485  true : false;
486  forwards = ((flags & SEARCH_FLAG_FORWARDS) != 0) ? true : false;
487  showall = ((flags & SEARCH_FLAG_SHOWALL) != 0) ? true : false;
488 
489  if (context->c == NULL)
490  return;
491 
492  if (context->is_html == true) {
493  html_content *html = (html_content *)context->c;
494 
495  box = html->layout;
496 
497  if (!box)
498  return;
499  }
500 
501 
502  /* check if we need to start a new search or continue an old one */
503  if ((context->newsearch) ||
504  (context->prev_case_sens != case_sensitive)) {
505  bool res;
506 
507  if (context->string != NULL)
508  free(context->string);
509 
510  context->current = NULL;
511  free_matches(context);
512 
513  context->string = malloc(string_len + 1);
514  if (context->string != NULL) {
515  memcpy(context->string, string, string_len);
516  context->string[string_len] = '\0';
517  }
518 
519  guit->search->hourglass(true, context->gui_p);
520 
521  if (context->is_html == true) {
522  res = find_occurrences_html(string, string_len,
523  box, case_sensitive, context);
524  } else {
525  res = find_occurrences_text(string, string_len,
526  context->c, case_sensitive, context);
527  }
528 
529  if (!res) {
530  free_matches(context);
531  guit->search->hourglass(false, context->gui_p);
532  return;
533  }
534  guit->search->hourglass(false, context->gui_p);
535 
536  context->prev_case_sens = case_sensitive;
537 
538  /* new search, beginning at the top of the page */
539  context->current = context->found->next;
540  context->newsearch = false;
541 
542  } else if (context->current != NULL) {
543  /* continued search in the direction specified */
544  if (forwards) {
545  if (context->current->next)
546  context->current = context->current->next;
547  } else {
548  if (context->current->prev)
549  context->current = context->current->prev;
550  }
551  }
552 
553  guit->search->status((context->current != NULL), context->gui_p);
554 
555  search_show_all(showall, context);
556 
557  guit->search->back_state((context->current != NULL) &&
558  (context->current->prev != NULL),
559  context->gui_p);
560  guit->search->forward_state((context->current != NULL) &&
561  (context->current->next != NULL),
562  context->gui_p);
563 
564  if (context->current == NULL)
565  return;
566 
567  if (context->is_html == true) {
568  /* get box position and jump to it */
569  box_coords(context->current->start_box, &bounds.x0, &bounds.y0);
570  /* \todo: move x0 in by correct idx */
571  box_coords(context->current->end_box, &bounds.x1, &bounds.y1);
572  /* \todo: move x1 in by correct idx */
573  bounds.x1 += context->current->end_box->width;
574  bounds.y1 += context->current->end_box->height;
575  } else {
577  context->current->start_idx,
578  context->current->end_idx, &bounds);
579  }
580 
581  msg_data.scroll.area = true;
582  msg_data.scroll.x0 = bounds.x0;
583  msg_data.scroll.y0 = bounds.y0;
584  msg_data.scroll.x1 = bounds.x1;
585  msg_data.scroll.y1 = bounds.y1;
586  content_broadcast(context->c, CONTENT_MSG_SCROLL, &msg_data);
587 }
588 
589 
590 /* Exported function documented in search.h */
591 void
592 search_step(struct search_context *context,
594  const char *string)
595 {
596  int string_len;
597  int i = 0;
598 
599  assert(context != NULL);
600 
601  guit->search->add_recent(string, context->gui_p);
602 
603  string_len = strlen(string);
604  for (i = 0; i < string_len; i++)
605  if (string[i] != '#' && string[i] != '*')
606  break;
607  if (i >= string_len) {
608  union content_msg_data msg_data;
609  free_matches(context);
610 
611  guit->search->status(true, context->gui_p);
612  guit->search->back_state(false, context->gui_p);
613  guit->search->forward_state(false, context->gui_p);
614 
615  msg_data.scroll.area = false;
616  msg_data.scroll.x0 = 0;
617  msg_data.scroll.y0 = 0;
618  content_broadcast(context->c, CONTENT_MSG_SCROLL, &msg_data);
619  return;
620  }
621  search_text(string, string_len, context, flags);
622 }
623 
624 
625 /* Exported function documented in search.h */
627  unsigned start_offset, unsigned end_offset,
628  unsigned *start_idx, unsigned *end_idx,
629  struct search_context *context)
630 {
631  if (c == context->c) {
632  struct list_entry *a;
633  for (a = context->found->next; a; a = a->next)
634  if (a->sel && selection_defined(a->sel) &&
636  start_offset, end_offset,
637  start_idx, end_idx))
638  return true;
639  }
640 
641  return false;
642 }
643 
644 
645 
646 
647 /* Exported function documented in search.h */
649 {
650  assert(context != NULL);
651 
652  if (context->string != NULL) {
653  guit->search->add_recent(context->string, context->gui_p);
654  free(context->string);
655  }
656 
657  guit->search->forward_state(true, context->gui_p);
658  guit->search->back_state(true, context->gui_p);
659 
660  free_matches(context);
661  free(context);
662 }
void selection_clear(struct selection *s, bool redraw)
Clears the current selection, optionally causing the screen to be updated.
Definition: selection.c:889
int y1
Bottom right.
Definition: types.h:42
void selection_destroy(struct selection *s)
Destroys a selection object, without updating the owning window (caller should call selection_clear()...
Definition: selection.c:154
struct selection * sel
Definition: search.c:60
void search_destroy_context(struct search_context *context)
Ends the search process, invalidating all state freeing the list of found boxes.
Definition: search.c:648
Interface to utility string handling.
Interface to content handler for plain text.
Localised message support (interface).
static nserror line(const struct redraw_context *ctx, const plot_style_t *style, const struct rect *line)
Plots a line.
Definition: plot.c:579
Interface to text/html content handler.
char * textplain_get_line(struct content *c, unsigned lineno, size_t *poffset, size_t *plen)
Return a pointer to the requested line of text.
Definition: textplain.c:1508
void selection_set_end(struct selection *s, unsigned offset)
Set the end position of the current selection, updating the screen.
Definition: selection.c:958
size_t byte_offset
Byte offset within a textual representation of this content.
Definition: box.h:368
#define selection_defined(s)
Definition: selection.h:75
struct list_entry * found
Definition: search.c:69
Interface to platform-specific miscellaneous browser operation table.
void box_coords(struct box *box, int *x, int *y)
Find the absolute coordinates of a box.
Definition: box_inspect.c:544
struct search_context * search_create_context(struct content *c, content_type type, void *gui_data)
create a search_context
Definition: search.c:80
struct box * layout
Box tree, or NULL.
Definition: private.h:142
HTML Box tree inspection interface.
High-level resource cache interface.
const char * type
Definition: filetype.cpp:44
struct selection * selection_create(struct content *c, bool is_html)
Creates a new selection object associated with a browser window.
Definition: selection.c:115
void(* status)(bool found, void *p)
Change the displayed search status.
Definition: search.h:38
void(* hourglass)(bool active, void *p)
display hourglass while searching.
Definition: search.h:46
void(* add_recent)(const char *string, void *p)
add search string to recent searches list front has full liberty how to implement the bare notificati...
Definition: search.h:56
Private data for text/html content.
static void free_matches(struct search_context *context)
Release the memory used by the list of matches, deleting selection objects too.
Definition: search.c:126
size_t length
Length of text.
Definition: box.h:358
struct list_entry * next
Definition: search.c:63
void selection_set_start(struct selection *s, unsigned offset)
Set the start position of the current selection, updating the screen.
Definition: selection.c:932
Content which corresponds to a single URL.
content_type
The type of a content.
Definition: content_type.h:53
struct content_msg_data::@108 scroll
CONTENT_MSG_SCROLL - Part of content to scroll to show.
Request to scroll content.
Definition: content_type.h:152
static struct list_entry * add_entry(unsigned start_idx, unsigned end_idx, struct search_context *context)
Add a new entry to the list of matches.
Definition: search.c:278
bool is_html
Definition: search.c:74
int x1
Definition: types.h:42
Interface to platform-specific search operations.
Box interface.
Content handling interface.
int y0
Top left.
Definition: types.h:41
void selection_init(struct selection *s, struct box *root, const nscss_len_ctx *len_ctx)
Initialise the selection object to use the given box subtree as its root, ie.
Definition: selection.c:205
bool newsearch
Definition: search.c:73
static void search_show_all(bool all, struct search_context *context)
Specifies whether all matches or just the current match should be highlighted in the search text...
Definition: search.c:426
struct box * end_box
Definition: search.c:58
void textplain_coords_from_range(struct content *c, unsigned start, unsigned end, struct rect *r)
Given a range of byte offsets within a UTF8 textplain content, return a box that fully encloses the t...
Definition: textplain.c:1454
struct box * start_box
Definition: search.c:57
char * string
Definition: search.c:71
struct list_entry * current
Definition: search.c:70
static nserror text(const struct redraw_context *ctx, const struct plot_font_style *fstyle, int x, int y, const char *text, size_t length)
Text plotting.
Definition: plot.c:978
void search_step(struct search_context *context, search_flags_t flags, const char *string)
Begins/continues the search process.
Definition: search.c:592
browser_window_console_flags flags
The flags of the logging.
Definition: content.h:73
bool selection_highlighted(const struct selection *s, unsigned start, unsigned end, unsigned *start_idx, unsigned *end_idx)
Tests whether a text range lies partially within the selection, if there is a selection defined...
Definition: selection.c:990
struct content * c
Definition: search.c:68
void content_broadcast(struct content *c, content_msg msg, const union content_msg_data *data)
Send a message to all users.
Definition: content.c:748
content is plain text
Definition: content_type.h:61
struct box * children
First child box, or NULL.
Definition: box.h:224
search_flags_t
Definition: search.h:27
struct gui_search_table * search
Page search table.
Definition: gui_table.h:113
unsigned start_idx
Definition: search.c:54
int height
Height of content box (excluding padding etc.).
Definition: box.h:291
Rectangle coordinates.
Definition: types.h:40
struct list_entry * prev
Definition: search.c:62
char * text
Text, or NULL if none.
Definition: box.h:353
void * gui_p
Definition: search.c:67
int width
Width of content box (excluding padding etc.).
Definition: box.h:287
unsigned long textplain_line_count(struct content *c)
Retrieve number of lines in content.
Definition: textplain.c:1356
struct hlcache_handle * object
Object in this box (usually an image), or NULL if none.
Definition: box.h:435
static bool find_occurrences_html(const char *pattern, int p_len, struct box *cur, bool case_sens, struct search_context *context)
Finds all occurrences of a given string in the html box tree.
Definition: search.c:317
Interface to HTML searching.
int x0
Definition: types.h:41
Interface to a number of general purpose functionality.
Definition: search.c:53
content is HTML
Definition: content_type.h:58
#define NOF_ELEMENTS(array)
Definition: search.c:49
bool search_term_highlighted(struct content *c, unsigned start_offset, unsigned end_offset, unsigned *start_idx, unsigned *end_idx, struct search_context *context)
Determines whether any portion of the given text box should be selected because it matches the curren...
Definition: search.c:626
Text selection within browser windows (interface).
struct box * next
Next sibling box, or NULL.
Definition: box.h:214
nscss_len_ctx len_ctx
CSS length conversion context for document.
Definition: private.h:115
void(* forward_state)(bool active, void *p)
activate search forwards button in gui
Definition: search.h:64
void(* back_state)(bool active, void *p)
activate search back button in gui
Definition: search.h:72
#define max(x, y)
Definition: utils.h:50
Data specific to CONTENT_HTML.
Definition: private.h:92
static bool find_occurrences_text(const char *pattern, int p_len, struct content *c, bool case_sens, struct search_context *context)
Finds all occurrences of a given string in a textplain content.
Definition: search.c:380
Interface to core interface table.
bool prev_case_sens
Definition: search.c:72
struct netsurf_table * guit
The global interface table.
Definition: gui_factory.c:46
unsigned end_idx
Definition: search.c:55
Extra data for some content_msg messages.
Definition: content.h:61
static void search_text(const char *string, int string_len, struct search_context *context, search_flags_t flags)
Search for a string in the box tree.
Definition: search.c:474
static const char * find_pattern(const char *string, int s_len, const char *pattern, int p_len, bool case_sens, unsigned int *m_len)
Find the first occurrence of &#39;match&#39; in &#39;string&#39; and return its index.
Definition: search.c:163
Node in box tree.
Definition: box.h:175