1 /*
2 * $LynxId: LYSearch.c,v 1.31 2012/02/09 19:02:53 tom Exp $
3 */
4 #include <HTUtils.h>
5 #include <HTAlert.h>
6 #include <LYUtils.h>
7 #include <LYStrings.h>
8 #include <LYSearch.h>
9 #include <LYGlobalDefs.h>
10 #include <GridText.h>
11
12 #include <LYLeaks.h>
13
link_has_target(int cur,char * target)14 static BOOL link_has_target(int cur,
15 char *target)
16 {
17 LinkInfo *a = &links[cur];
18 OptionType *option;
19 char *stars = NULL;
20 const char *cp;
21 int count;
22
23 /*
24 * Search the hightext strings, if present, taking the LYcase_sensitive
25 * setting into account.
26 */
27 for (count = 0;; ++count) {
28 const char *text = LYGetHiliteStr(cur, count);
29
30 if (text == NULL)
31 break;
32 if (LYno_attr_strstr(text, target))
33 return TRUE;
34 }
35
36 /*
37 * Search the relevant form fields, taking the LYcase_sensitive setting into
38 * account. - FM
39 */
40 if ((a->l_form != NULL && a->l_form->value != NULL) &&
41 a->l_form->type != F_HIDDEN_TYPE) {
42 if (a->l_form->type == F_PASSWORD_TYPE) {
43 /*
44 * Check the actual, hidden password, and then the displayed
45 * string. - FM
46 */
47 if (LYno_attr_strstr(a->l_form->value, target)) {
48 return TRUE;
49 }
50 StrAllocCopy(stars, a->l_form->value);
51 memset(stars, '*', strlen(stars));
52 if (LYno_attr_strstr(stars, target)) {
53 FREE(stars);
54 return TRUE;
55 }
56 FREE(stars);
57 } else if (a->l_form->type == F_OPTION_LIST_TYPE) {
58 /*
59 * Search the option strings that are displayed when the popup is
60 * invoked. - FM
61 */
62 option = a->l_form->select_list;
63 while (option != NULL) {
64 if (LYno_attr_strstr(option->name, target)) {
65 return TRUE;
66 }
67 option = option->next;
68 }
69 } else if (a->l_form->type == F_RADIO_TYPE) {
70 /*
71 * Search for checked or unchecked parens. - FM
72 */
73 if (a->l_form->num_value) {
74 cp = checked_radio;
75 } else {
76 cp = unchecked_radio;
77 }
78 if (LYno_attr_strstr(cp, target)) {
79 return TRUE;
80 }
81 } else if (a->l_form->type == F_CHECKBOX_TYPE) {
82 /*
83 * Search for checked or unchecked square brackets. - FM
84 */
85 if (a->l_form->num_value) {
86 cp = checked_box;
87 } else {
88 cp = unchecked_box;
89 }
90 if (LYno_attr_strstr(cp, target)) {
91 return TRUE;
92 }
93 } else {
94 /*
95 * Check the values intended for display. May have been found
96 * already via the hightext search, but make sure here that the
97 * entire value is searched. - FM
98 */
99 if (LYno_attr_strstr(a->l_form->value, target)) {
100 return TRUE;
101 }
102 }
103 }
104 return FALSE;
105 }
106
107 /*
108 * Search for the target string inside of the links that are currently
109 * displayed on the screen beginning with the one after the currently selected
110 * one. If found set cur to the new value and return TRUE. If not found do
111 * not reset cur and return FALSE.
112 */
113
check_next_target_in_links(int * cur,char * target)114 static int check_next_target_in_links(int *cur,
115 char *target)
116 {
117 int i;
118
119 if (nlinks != 0) {
120 for (i = *cur + 1; i < nlinks; ++i) {
121 if (link_has_target(i, target)) {
122 *cur = i;
123 return TRUE;
124 }
125 }
126 }
127 return FALSE;
128 }
129
check_prev_target_in_links(int * cur,char * target)130 static int check_prev_target_in_links(int *cur,
131 char *target)
132 {
133 int i;
134
135 if (nlinks != 0) {
136 for (i = *cur - 1; i >= 0; --i) {
137 if (link_has_target(i, target)) {
138 *cur = i;
139 return TRUE;
140 }
141 }
142 }
143 return FALSE;
144 }
145
146 /*
147 * Textsearch checks the prev_target variable to see if it is empty. If it is
148 * then it requests a new search string. It then searches the current file for
149 * the next instance of the search string and finds the line number that the
150 * string is on
151 *
152 * This is the primary USER search engine and is case sensitive or case
153 * insensitive depending on the 'LYcase_sensitive' global variable
154 */
textsearch(DocInfo * cur_doc,bstring ** prev_target,int direction)155 BOOL textsearch(DocInfo *cur_doc,
156 bstring **prev_target,
157 int direction)
158 {
159 int offset;
160 int oldcur = cur_doc->link;
161 static bstring *my_prev_target = NULL;
162 static BOOL first = TRUE;
163 char *cp;
164 int ch = 0;
165 RecallType recall;
166 int QueryTotal;
167 int QueryNum;
168 BOOLEAN FirstRecall = TRUE;
169
170 /*
171 * Initialize the search string buffer. - FM
172 */
173 if (first) {
174 BStrCopy0(my_prev_target, "");
175 first = FALSE;
176 }
177
178 QueryTotal = (search_queries ? HTList_count(search_queries) : 0);
179 recall = ((QueryTotal >= 1) ? RECALL_URL : NORECALL);
180 QueryNum = QueryTotal;
181
182 if (direction != 0) {
183 /*
184 * LYK_NEXT or LYK_PREV was pressed, so copy the buffer into
185 * prev_target.
186 */
187 BStrCopy(*prev_target, my_prev_target);
188 } else if (*prev_target == 0) {
189 BStrCopy0(*prev_target, "");
190 }
191
192 if (strlen((*prev_target)->str) == 0) {
193 /*
194 * This is a new WHEREIS search ('/'), or LYK_NEXT was pressed but
195 * there was no previous search, so we need to get a search string from
196 * the user. - FM
197 */
198 _statusline(ENTER_WHEREIS_QUERY);
199
200 ch = LYgetBString(prev_target, VISIBLE, 0, recall);
201 if (ch < 0) {
202 /*
203 * User cancelled the search via ^G. Restore prev_target and
204 * return. - FM
205 */
206 BStrCopy(*prev_target, my_prev_target);
207 HTInfoMsg(CANCELLED);
208 return (FALSE);
209 }
210 }
211
212 check_recall:
213 if (strlen((*prev_target)->str) == 0 &&
214 !(recall && (ch == UPARROW || ch == DNARROW))) {
215 /*
216 * No entry. Simply return, retaining the current buffer. Because
217 * prev_target is now reset, highlighting of the previous search string
218 * will no longer occur, but it can be used again via LYK_NEXT or
219 * LYK_PREV.
220 */
221 HTInfoMsg(CANCELLED);
222 return (FALSE);
223 }
224
225 if (recall && ch == UPARROW) {
226 if (FirstRecall) {
227 /*
228 * Use the current string or last query in the list. - FM
229 */
230 FirstRecall = FALSE;
231 if (!isBEmpty(my_prev_target)) {
232 for (QueryNum = (QueryTotal - 1); QueryNum > 0; QueryNum--) {
233 if ((cp = (char *) HTList_objectAt(search_queries,
234 QueryNum)) != NULL &&
235 !strcmp(my_prev_target->str, cp)) {
236 break;
237 }
238 }
239 } else {
240 QueryNum = 0;
241 }
242 } else {
243 /*
244 * Go back to the previous query in the list. - FM
245 */
246 QueryNum++;
247 }
248 if (QueryNum >= QueryTotal)
249 /*
250 * Roll around to the last query in the list. - FM
251 */
252 QueryNum = 0;
253 if ((cp = (char *) HTList_objectAt(search_queries,
254 QueryNum)) != NULL) {
255 BStrCopy0(*prev_target, cp);
256 if (!isBEmpty(my_prev_target) &&
257 !strcmp(my_prev_target->str, (*prev_target)->str)) {
258 _statusline(EDIT_CURRENT_QUERY);
259 } else if ((!isBEmpty(my_prev_target) && QueryTotal == 2) ||
260 (isBEmpty(my_prev_target) && QueryTotal == 1)) {
261 _statusline(EDIT_THE_PREV_QUERY);
262 } else {
263 _statusline(EDIT_A_PREV_QUERY);
264 }
265 ch = LYgetBString(prev_target, VISIBLE, 0, recall);
266 if (ch < 0) {
267 /*
268 * User canceled the search via ^G. Restore prev_target and
269 * return. - FM
270 */
271 BStrCopy(*prev_target, my_prev_target);
272 HTInfoMsg(CANCELLED);
273 return (FALSE);
274 }
275 goto check_recall;
276 }
277 } else if (recall && ch == DNARROW) {
278 if (FirstRecall) {
279 /*
280 * Use the current string or first query in the list. - FM
281 */
282 FirstRecall = FALSE;
283 if (!isBEmpty(my_prev_target)) {
284 for (QueryNum = 0; QueryNum < (QueryTotal - 1); QueryNum++) {
285 if ((cp = (char *) HTList_objectAt(search_queries,
286 QueryNum)) != NULL &&
287 !strcmp(my_prev_target->str, cp)) {
288 break;
289 }
290 }
291 } else {
292 QueryNum = QueryTotal - 1;
293 }
294 } else {
295 /*
296 * Advance to the next query in the list. - FM
297 */
298 QueryNum--;
299 }
300 if (QueryNum < 0)
301 /*
302 * Roll around to the first query in the list. - FM
303 */
304 QueryNum = QueryTotal - 1;
305 if ((cp = (char *) HTList_objectAt(search_queries,
306 QueryNum)) != NULL) {
307 BStrCopy0(*prev_target, cp);
308 if (!isBEmpty(my_prev_target) &&
309 !strcmp(my_prev_target->str, (*prev_target)->str)) {
310 _statusline(EDIT_CURRENT_QUERY);
311 } else if ((!isBEmpty(my_prev_target) && QueryTotal == 2) ||
312 (isBEmpty(my_prev_target) && QueryTotal == 1)) {
313 _statusline(EDIT_THE_PREV_QUERY);
314 } else {
315 _statusline(EDIT_A_PREV_QUERY);
316 }
317 ch = LYgetBString(prev_target, VISIBLE, 0, recall);
318 if (ch < 0) {
319 /*
320 * User cancelled the search via ^G. Restore prev_target and
321 * return. - FM
322 */
323 BStrCopy(*prev_target, my_prev_target);
324 HTInfoMsg(CANCELLED);
325 return (FALSE);
326 }
327 goto check_recall;
328 }
329 }
330 /*
331 * Replace the search string buffer with the new target. - FM
332 */
333 BStrCopy(my_prev_target, *prev_target);
334 HTAddSearchQuery(my_prev_target->str);
335
336 if (direction < 0) {
337 offset = 0;
338 if (check_prev_target_in_links(&cur_doc->link, (*prev_target)->str)) {
339 /*
340 * Found in link, changed cur, we're done.
341 */
342 LYhighlight(FALSE, oldcur, (*prev_target)->str);
343 return (TRUE);
344 }
345 } else {
346
347 /*
348 * Search the links on the currently displayed page for the string,
349 * starting after the current link. - FM
350 */
351 if (check_next_target_in_links(&cur_doc->link, (*prev_target)->str)) {
352 /*
353 * Found in link, changed cur, we're done.
354 */
355 LYhighlight(FALSE, oldcur, (*prev_target)->str);
356 return (TRUE);
357 }
358
359 /*
360 * We'll search the text starting from the link we are on, or the next
361 * page.
362 */
363 if (nlinks == 0)
364 offset = (display_lines - 1);
365 else
366 offset = links[cur_doc->link].ly - 1;
367 }
368
369 /*
370 * Resume search, this time for all text. Set www_search_result if string
371 * found, and position the hit near top of screen.
372 */
373 www_user_search((cur_doc->line + offset), cur_doc, (*prev_target)->str, direction);
374 if (cur_doc->link != oldcur) {
375 LYhighlight(FALSE, oldcur, (*prev_target)->str);
376 return (TRUE);
377 }
378 return (BOOL) (www_search_result > 0);
379 }
380