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