+/*
+ * Searching
+ */
+
+static void search_view(struct view *view, enum request request, const char *search);
+
+static bool
+find_next_line(struct view *view, unsigned long lineno, struct line *line)
+{
+ if (!view->ops->grep(view, line))
+ return FALSE;
+
+ if (lineno - view->offset >= view->height) {
+ view->offset = lineno;
+ view->lineno = lineno;
+ redraw_view(view);
+
+ } else {
+ unsigned long old_lineno = view->lineno - view->offset;
+
+ view->lineno = lineno;
+
+ wmove(view->win, old_lineno, 0);
+ wclrtoeol(view->win);
+ draw_view_line(view, old_lineno);
+
+ draw_view_line(view, view->lineno - view->offset);
+ redrawwin(view->win);
+ wrefresh(view->win);
+ }
+
+ report("Line %ld matches '%s'", lineno + 1, view->grep);
+ return TRUE;
+}
+
+static void
+find_next(struct view *view, enum request request)
+{
+ unsigned long lineno = view->lineno;
+ int direction;
+
+ if (!*view->grep) {
+ if (!*opt_search)
+ report("No previous search");
+ else
+ search_view(view, request, opt_search);
+ return;
+ }
+
+ switch (request) {
+ case REQ_SEARCH:
+ case REQ_FIND_NEXT:
+ direction = 1;
+ break;
+
+ case REQ_SEARCH_BACK:
+ case REQ_FIND_PREV:
+ direction = -1;
+ break;
+
+ default:
+ return;
+ }
+
+ if (request == REQ_FIND_NEXT || request == REQ_FIND_PREV)
+ lineno += direction;
+
+ /* Note, lineno is unsigned long so will wrap around in which case it
+ * will become bigger than view->lines. */
+ for (; lineno < view->lines; lineno += direction) {
+ struct line *line = &view->line[lineno];
+
+ if (find_next_line(view, lineno, line))
+ return;
+ }
+
+ report("No match found for '%s'", view->grep);
+}
+
+static void
+search_view(struct view *view, enum request request, const char *search)
+{
+ int regex_err;
+
+ if (*view->grep) {
+ regfree(&view->regex);
+ *view->grep = 0;
+ }
+
+ regex_err = regcomp(&view->regex, search, REG_EXTENDED);
+ if (regex_err != 0) {
+ char buf[SIZEOF_STR] = "unknown error";
+
+ regerror(regex_err, &view->regex, buf, sizeof(buf));
+ report("Search failed: %s", buf);;
+ return;
+ }
+
+ string_copy(view->grep, search);
+
+ find_next(view, request);
+}
+