/* -*- mode: C; mode: fold -*- */ /* This file is part of MOST. Copyright (c) 1991, 1999, 2002, 2005-2018, 2019 John E. Davis This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "config.h" #include #include #include #include #include "most.h" #include "window.h" #include "line.h" #include "file.h" #include "display.h" #include "search.h" /* Note!!! The regular expression searches may not work. I have not * tested them. * FIXME!!! This whole thing needs to be rewritten. It is a mess. */ int Most_Case_Sensitive = 0; char Most_Search_Str[256]; int Most_Search_Dir = 1; #include "jdmacros.h" typedef struct Search_Type_ Search_Type; struct Search_Type_ { void *cd; unsigned char *(*fsearch_method) (Search_Type *, unsigned char *, unsigned char *, unsigned char *); unsigned char *(*bsearch_method) (Search_Type *, unsigned char *, unsigned char *, unsigned char *); void (*close_method)(Search_Type *); }; static unsigned char Ascii_Upper[256]; static void bs_search_init (void) { static int inited = 0; unsigned int i; if (inited) return; for (i = 0; i < 256; i++) Ascii_Upper[i] = i; for (i = 'a'; i <= 'z'; i++) Ascii_Upper[i] = (i - 32); inited = 1; } #define UPCASE(ch) (Ascii_Upper[(ch)]) #define CHAR_EQS(a,b) \ (((a) == (b)) || (!Most_Case_Sensitive && (UPCASE(a) == UPCASE(b)))) static int is_ansi_escape (unsigned char **begp, unsigned char *end) { unsigned char *p, ch; p = *begp; if ((p == end) || (*p++ != '[')) return 0; /* Look for "ESC X m", where X is one of: * X = "" * X = digit * X = digit ; digit ... */ ch = *p++; while (isdigit (ch)) { while ((p < end) && isdigit (*p)) p++; if (p == end) return 0; ch = *p++; if (ch == 'm') break; if ((ch != ';') || (p == end)) return 0; ch = *p++; } if (ch != 'm') return 0; *begp = p; return 1; } static int is_rev_ansi_escape (unsigned char *beg, unsigned char **endp) { unsigned char *p, ch; p = *endp; if (p == beg) return 0; ch = *p--; while (isdigit (ch)) { while ((p > beg) && isdigit (*p)) p--; if (p == beg) return 0; ch = *p--; if (ch == '[') break; if ((ch != ';') || (p == beg)) return 0; ch = *p--; } if ((ch != '[') || (p < beg) || (*p != 033)) return 0; *endp = p-1; return 1; } /* These routines have special processing for ANSI escape sequence and backspace handling. * For example, "hello world" may occur as: * plain: hello world * underlined: h_e_l_l_o_ world * underlined: _h_e_l_l_o world * bold: hheelllloo world * ansi: hello world * ansi: hello world */ /* This routine returns the 1 + position of first match of key in str. * searches from beg up to but not including end. Handles backspace, etc */ static unsigned char * bs_fsearch (Search_Type *st, unsigned char *beg, unsigned char *end, unsigned char *key) { unsigned char ch, ch1, ch1up; unsigned char *pos; int cis, key_len, j, str_len; (void) st; key_len = strlen ((char *)key); if (key_len == 0) return Most_Eob; str_len = (int) (end - beg); if (str_len < key_len) return (Most_Eob); cis = (Most_Case_Sensitive == 0); ch1 = key[0]; ch1up = UPCASE(ch1); while (1) { /* Find first character that matches */ while (1) { if (beg == end) return Most_Eob; ch = *beg++; if ((ch == ch1) || (cis && (ch1up == UPCASE(ch)))) break; } /* so we have a position of possible match */ j = 1; pos = beg; /* save this position so we start from here again */ while (1) { if (j == key_len) return pos - 1; if (beg == end) break; ch = *beg++; /* FIXME: This only works for x^Hx but not x^Hx^Hx... * It is probably better to skip all the ^H characters * until the end. That is, regard "a^Hb^Hc" as 'c'. */ if ((ch == 8) && (beg + 1 < end) && (CHAR_EQS(key[j - 1], *beg) || (*beg == '_'))) { ch = *(beg + 1); beg += 2; } else if ((ch == '_') && (beg + 1 < end)) { ch = *beg++; if (ch == 8) ch = *beg++; else { ch = '_'; beg--; } } else if ((ch == 033) && is_ansi_escape (&beg, end)) continue; if (!CHAR_EQS(ch, key[j])) break; j++; } beg = pos; } } /* * Search backwards in the buffer "beg" up to, but not including "end" for * pattern "key". It handles backspaces, etc */ static unsigned char * bs_bsearch (Search_Type *st, unsigned char *beg, unsigned char *end, unsigned char *key) { unsigned char ch, ch1, ch1up; unsigned char *pos; int key_len,j, str_len; int cis; (void) st; key_len = strlen ((char *)key); if (key_len == 0) return Most_Eob; str_len = (int) (end - beg); if (str_len < key_len) return Most_Eob; ch1 = key[key_len-1]; ch1up = UPCASE(ch1); cis = (Most_Case_Sensitive == 0); while (1) { while (1) { if (end < beg) return Most_Eob; ch = *end--; if ((ch == ch1) || (cis && (ch1up == UPCASE(ch)))) break; } pos = end; /* save this position so we start from here again */ j = key_len - 2; while (1) { if (j < 0) return end + 1; if (end < beg) break; ch = *end--; if ((ch == 8) && (end >= beg + 1) && (CHAR_EQS(key[j + 1], *end) || (*end == '_'))) { ch = *(end - 1); end -= 2; } else if ((ch == '_') && (end >= beg + 1)) { ch = *end--; if (ch == 8) ch = *end--; else { ch = '_'; end++; } } else if ((ch == 'm') && is_rev_ansi_escape (beg, &end)) continue; if (!CHAR_EQS(ch, key[j])) break; j--; } end = pos; } } static int bs_open_search (Search_Type *st, char *key) { (void) key; bs_search_init (); st->fsearch_method = bs_fsearch; st->bsearch_method = bs_bsearch; st->close_method = NULL; st->cd = NULL; return 0; } static unsigned char *sl_fsearch (Search_Type *st, unsigned char *beg, unsigned char *end, unsigned char *key) { unsigned char *p; (void) key; if (NULL == (p = SLsearch_forward ((SLsearch_Type *)st->cd, beg, end))) p = Most_Eob; return p; } static unsigned char *sl_bsearch (Search_Type *st, unsigned char *beg, unsigned char *end, unsigned char *key) { unsigned char *p; (void) key; if (NULL == (p = SLsearch_backward ((SLsearch_Type *)st->cd, beg, end, end))) p = Most_Eob; return p; } static void sl_search_close (Search_Type *st) { if (st->cd != NULL) SLsearch_delete ((SLsearch_Type *) st->cd); } static int sl_open_search (Search_Type *st, char *key) { unsigned int flags = 0; if (Most_Case_Sensitive == 0) flags |= SLSEARCH_CASELESS; if (Most_UTF8_Mode) flags |= SLSEARCH_UTF8; if (NULL == (st->cd = SLsearch_new ((SLuchar_Type *) key, flags))) return -1; st->fsearch_method = sl_fsearch; st->bsearch_method = sl_bsearch; st->close_method = sl_search_close; return 0; } static int do_search_internal (Search_Type *st, unsigned char *from, int repeat, MOST_INT *col) { /* return the line match was found as well as line number, * search from i on; assume that line_array match the i so we need * no initial lookup */ int test; MOST_INT save_line, the_col, row, s_len; char string[300]; unsigned char *pos, *eob; unsigned int save_ofs; unsigned int found_ofs; if (*Most_Search_Str == 0) { most_message("Search string not specified.",1); return -1; } if ((from < Most_Beg) || (from > Most_Eob)) return -1; save_ofs = Most_C_Offset; save_line = Most_C_Line; found_ofs = Most_Eob - Most_Beg; *col = 0; s_len = strlen (Most_Search_Str); pos = from; eob = Most_Eob; test = repeat && (pos < Most_Eob) && (pos >= Most_Beg); while(test) { if (Most_Search_Dir == 1) { while (1) { unsigned int pos_ofs; pos = (*st->fsearch_method)(st, pos, Most_Eob, (unsigned char*) Most_Search_Str); pos_ofs = (unsigned int) (Most_Eob - Most_Beg); if (pos < Most_Eob) break; if (0 == most_read_file_dsc (10, 0)) { /* Pointer may be invalid after this call */ pos = Most_Beg + pos_ofs; break; } /* This might need an adjustment */ pos = Most_Beg + (pos_ofs - s_len); if (pos < Most_Beg) pos = Most_Beg; } } else pos = (*st->bsearch_method)(st, Most_Beg, pos, (unsigned char *) Most_Search_Str); if (pos < Most_Eob) { repeat--; found_ofs = pos - Most_Beg; if (Most_Search_Dir == 1) pos += s_len; else pos--; } test = repeat && (pos < Most_Eob) && (pos >= Most_Beg); if (SLKeyBoard_Quit) { most_message ("Search Interrupted.", 1); break; } } if (eob != Most_Eob) Most_Num_Lines = most_count_lines (Most_Beg, Most_Eob); if (repeat) /* not found */ { *col = 0; (void) sprintf(string,"Search failed: %s",Most_Search_Str); most_message(string,1); row = -1; } else /* if ( !Most_T_Opt && !Most_B_Opt) */ /* expand tabs to get col correct */ { most_find_row_column(Most_Beg + found_ofs, &row, &the_col); if (Most_B_Opt) *col = the_col + 52; else *col = 1 + most_apparant_distance(Most_Beg + found_ofs); } Most_C_Offset = save_ofs; Most_C_Line = save_line; if (row > 0) Most_Curs_Offset = found_ofs; return row; } static int search_internal (Search_Type *st, unsigned char *from, int repeat, MOST_INT *colp) { int status; status = do_search_internal (st, from, repeat, colp); if (st->close_method != NULL) (st->close_method)(st); return status; } static int simple_search (unsigned char *from, int repeat, MOST_INT *colp) { Search_Type st; if (Most_V_Opt || Most_B_Opt) { /* Nothing special about the ^H and _ chars. User faster SLsearch */ if (-1 == sl_open_search (&st, Most_Search_Str)) return -1; } else if (-1 == bs_open_search (&st, Most_Search_Str)) return -1; return search_internal (&st, from, repeat, colp); } static void re_search_close (Search_Type *st) { if (st->cd != NULL) SLregexp_free ((SLRegexp_Type *) st->cd); } static unsigned char * re_fsearch (Search_Type *st, unsigned char *beg, unsigned char *end, unsigned char *key) { SLRegexp_Type *re; unsigned char *p; unsigned int flags; (void) key; re = (SLRegexp_Type *)st->cd; (void) SLregexp_get_hints (re, &flags); while (beg < end) { unsigned char *line_end = beg; while (line_end < end) { unsigned char ch = *line_end++; if (ch == '\n') break; } p = (unsigned char *)SLregexp_match (re, (char *)beg, (line_end - beg)); if (p != NULL) { if ((0 == (flags & SLREGEXP_HINT_BOL)) || (p != beg) || (beg == Most_Beg) || (*(beg - 1) == '\n')) return p; } beg = line_end; } return Most_Eob; } static unsigned char * re_bsearch (Search_Type *st, unsigned char *beg, unsigned char *end, unsigned char *key) { SLRegexp_Type *re; unsigned char *line_end, *eob; unsigned int flags; (void) key; re = (SLRegexp_Type *)st->cd; (void) SLregexp_get_hints (re, &flags); line_end = end; eob = Most_Eob; while (line_end < eob) { if (*line_end == '\n') break; line_end++; } while (end > beg) { unsigned char *p, *match; unsigned char *line = end; while (line > beg) { line--; if (*line == '\n') { line++; break; } } /* line is now at the start of a line */ if (NULL != (match = (unsigned char *)SLregexp_match (re, (char *)line, line_end-line))) { if (match >= end) { /* Match occurs to right of boundary. Try previous line */ end = line_end = line-1; continue; } if (flags & SLREGEXP_HINT_BOL) return match; /* t tt z t t t z */ /* Find match closest to end */ while ((line < end) && (NULL != (p = (unsigned char *)SLregexp_match (re, (char *)line, (line_end - line)))) && (p < end)) { match = p; line++; } return match; } end = line-1; line_end = end; } return Most_Eob; } static int regexp_search (unsigned char *from, int repeat, MOST_INT *colp) { Search_Type st; SLRegexp_Type *re; char *pattern; unsigned int flags; pattern = Most_Search_Str; flags = 0; if (Most_Case_Sensitive == 0) flags |= SLREGEXP_CASELESS; re = SLregexp_compile (pattern, flags); if (re == NULL) return -1; (void) SLregexp_get_hints (re, &flags); if (flags & SLREGEXP_HINT_OSEARCH) { SLregexp_free (re); return simple_search (from, repeat, colp); } st.cd = (void *)re; st.fsearch_method = re_fsearch; st.bsearch_method = re_bsearch; st.close_method = re_search_close; return search_internal (&st, from, repeat, colp); } int most_search (unsigned char *from, int repeat, MOST_INT *colp) { if (Most_Do_Regexp_Search) return regexp_search (from, repeat, colp); return simple_search (from, repeat, colp); }