rust-most/vendor/most-5.1.0/src/search.c

668 lines
14 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* -*- 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 <stdio.h>
#include <string.h>
#include <ctype.h>
#include <slang.h>
#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);
}