1 /*
2    Copyright (c) 2000-2001 Perry Rapp
3 
4    Permission is hereby granted, free of charge, to any person
5    obtaining a copy of this software and associated documentation
6    files (the "Software"), to deal in the Software without
7    restriction, including without limitation the rights to use, copy,
8    modify, merge, publish, distribute, sublicense, and/or sell copies
9    of the Software, and to permit persons to whom the Software is
10    furnished to do so, subject to the following conditions:
11 
12    The above copyright notice and this permission notice shall be
13    included in all copies or substantial portions of the Software.
14 
15    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
19    BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
20    ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21    CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22    SOFTWARE.
23 */
24 /*================================================================
25  * scan.c -- Search database with full scan (several types)
26  * Copyright(c) 2000-2001 by Perry Rapp; all rights reserved
27  *   Created: 2000/12
28  *==============================================================*/
29 
30 #include "sys_inc.h"
31 #include <stdarg.h>
32 #include "llstdlib.h"
33 #include "table.h"
34 #include "indiseq.h"
35 #include "feedback.h"
36 #include "liflines.h"
37 #include "fpattern.h"
38 
39 #include "llinesi.h"
40 
41 /*********************************************
42  * external variables (no header)
43  *********************************************/
44 
45 extern STRING qSscanrs, qSscnrfn;
46 
47 /*********************************************
48  * local types
49  *********************************************/
50 
51 typedef struct
52 {
53 	INT scantype;
54 	CNSTRING statusmsg;
55 	char pattern[64];
56 	INDISEQ seq;
57 	STRING field; /* field to scan, eg "AUTH" for sources by author */
58 	BOOLEAN conts; /* include CONC & CONT children tags? */
59 } SCANNER;
60 
61 /*********************************************
62  * local enums
63  *********************************************/
64 
65 static INT SCAN_NAME_FULL=0;
66 static INT SCAN_NAME_FRAG=1;
67 static INT SCAN_REFN=2;
68 static INT SCAN_SRC_AUTH=3;
69 static INT SCAN_SRC_TITL=4;
70 
71 /*********************************************
72  * local function prototypes
73  *********************************************/
74 
75 static void do_fields_scan(SCANNER * scanner, RECORD rec);
76 static void do_name_scan(SCANNER * scanner, STRING prompt);
77 static void do_sources_scan(SCANNER * scanner, CNSTRING prompt);
78 static BOOLEAN ns_callback(CNSTRING key, CNSTRING name, BOOLEAN newset, void *param);
79 static BOOLEAN rs_callback(CNSTRING key, CNSTRING refn, BOOLEAN newset, void *param);
80 static void scanner_add_result(SCANNER * scanner, CNSTRING key);
81 static BOOLEAN scanner_does_pattern_match(SCANNER *scanner, CNSTRING text);
82 static INDISEQ scanner_free_and_return_seq(SCANNER * scanner);
83 static void scanner_init(SCANNER * scanner, INT scantype, CNSTRING statusmsg);
84 static void scanner_scan_titles(SCANNER * scanner);
85 static void scanner_set_field(SCANNER * scanner, STRING field);
86 static BOOLEAN scanner_set_pattern(SCANNER * scanner, STRING pattern);
87 
88 /*********************************************
89  * local function definitions
90  * body of module
91  *********************************************/
92 
93 /*==============================================
94  * name_fragment_scan -- Ask for pattern and search all persons by name
95  *  sts: [IN]  status to show during scan
96  *============================================*/
97 INDISEQ
name_fragment_scan(STRING sts)98 name_fragment_scan (STRING sts)
99 {
100 	SCANNER scanner;
101 	scanner_init(&scanner, SCAN_NAME_FRAG, sts);
102 	do_name_scan(&scanner, _("Enter pattern to match against single surname or given name."));
103 	return scanner_free_and_return_seq(&scanner);
104 }
105 /*======================================
106  * full_name_scan -- Ask for pattern and search all persons by full name
107  *  sts: [IN]  status to show during scan
108  *====================================*/
109 INDISEQ
full_name_scan(STRING sts)110 full_name_scan (STRING sts)
111 {
112 	SCANNER scanner;
113 	scanner_init(&scanner, SCAN_NAME_FULL, sts);
114 	do_name_scan(&scanner, _("Enter pattern to match against full name."));
115 	return scanner_free_and_return_seq(&scanner);
116 }
117 /*==============================
118  * refn_scan -- Ask for pattern and search all refns
119  *  sts: [IN]  status to show during scan
120  *============================*/
121 INDISEQ
refn_scan(STRING sts)122 refn_scan (STRING sts)
123 {
124 	SCANNER scanner;
125 	scanner_init(&scanner, SCAN_REFN, sts);
126 
127 	while (1) {
128 		char request[MAXPATHLEN];
129 		STRING prompt = _("Enter pattern to match against refn.");
130 		BOOLEAN rtn = ask_for_string(prompt, _("pattern: "),
131 			request, sizeof(request));
132 		if (!rtn || !request[0])
133 			return scanner_free_and_return_seq(&scanner);
134 		if (scanner_set_pattern(&scanner, request))
135 			break;
136 	}
137 	msg_status(sts);
138 	traverse_refns(rs_callback, &scanner);
139 	msg_status("");
140 
141 	return scanner_free_and_return_seq(&scanner);
142 }
143 /*==============================
144  * scan_souce_by_author -- Ask for pattern and search all sources by author
145  *  sts: [IN]  status to show during scan
146  *============================*/
147 INDISEQ
scan_souce_by_author(STRING sts)148 scan_souce_by_author (STRING sts)
149 {
150 	SCANNER scanner;
151 	scanner_init(&scanner, SCAN_SRC_AUTH, sts);
152 	scanner_set_field(&scanner, "AUTH");
153 	do_sources_scan(&scanner, _("Enter pattern to match against author."));
154 	return scanner_free_and_return_seq(&scanner);
155 
156 }
157 /*==============================
158  * scan_souce_by_title -- Ask for pattern and search all sources by title
159  *  sts: [IN]  status to show during scan
160  *============================*/
161 INDISEQ
scan_souce_by_title(STRING sts)162 scan_souce_by_title (STRING sts)
163 {
164 	SCANNER scanner;
165 	scanner_init(&scanner, SCAN_SRC_TITL, sts);
166 	scanner_scan_titles(&scanner);
167 	do_sources_scan(&scanner, _("Enter pattern to match against title."));
168 	return scanner_free_and_return_seq(&scanner);
169 }
170 /*==============================
171  * do_name_scan -- traverse names looking for pattern matching
172  *  scanner:   [I/O] all necessary scan info, including sequence of results
173  *  prompt:    [IN]  appropriate prompt to ask for pattern
174  *============================*/
175 static void
do_name_scan(SCANNER * scanner,STRING prompt)176 do_name_scan (SCANNER * scanner, STRING prompt)
177 {
178 	while (1) {
179 		char request[MAXPATHLEN];
180 		BOOLEAN rtn = ask_for_string(prompt, _("pattern: "),
181 			request, sizeof(request));
182 		if (!rtn || !request[0])
183 			return;
184 		if (scanner_set_pattern(scanner, request))
185 			break;
186 	}
187 	msg_status((STRING)scanner->statusmsg);
188 	traverse_names(ns_callback, scanner);
189 	msg_status("");
190 }
191 /*==============================
192  * do_sources_scan -- traverse sources looking for pattern matching
193  *  scanner:   [I/O] all necessary scan info, including sequence of results
194  *  prompt:    [IN]  appropriate prompt to ask for pattern
195  *============================*/
196 static void
do_sources_scan(SCANNER * scanner,CNSTRING prompt)197 do_sources_scan (SCANNER * scanner, CNSTRING prompt)
198 {
199 	INT keynum = 0;
200 
201 	while (1) {
202 		char request[MAXPATHLEN];
203 		BOOLEAN rtn = ask_for_string(prompt, _("pattern: "),
204 			request, sizeof(request));
205 		if (!rtn || !request[0])
206 			return;
207 		if (scanner_set_pattern(scanner, request))
208 			break;
209 	}
210 	/* msg_status takes STRING arg, should take CNSTRING - const declaration error */
211 	msg_status((STRING)scanner->statusmsg);
212 
213 	while (1) {
214 		RECORD rec = 0;
215 		keynum = xref_nexts(keynum);
216 		if (!keynum)
217 			break;
218 		rec = keynum_to_srecord(keynum);
219 		do_fields_scan(scanner, rec);
220 	}
221 	msg_status("");
222 }
223 /*==============================
224  * do_fields_scan -- traverse top nodes looking for desired field value
225  *  scanner:   [I/O] all necessary scan info, including sequence of results
226  *  rec:       [IN]  record to search
227  *============================*/
228 static void
do_fields_scan(SCANNER * scanner,RECORD rec)229 do_fields_scan (SCANNER * scanner, RECORD rec)
230 {
231 	/* NB: Only scanning top-level nodes right now */
232 	NODE node = nztop(rec);
233 	for (node = nchild(node); node; node = nsibling(node)) {
234 		STRING tag = ntag(node);
235 		if (tag && eqstr(tag, scanner->field)) {
236 			STRING val = nval(node);
237 			if (val && scanner_does_pattern_match(scanner, val)) {
238 				CNSTRING key = nzkey(rec);
239 				scanner_add_result(scanner, key);
240 				return;
241 			}
242 			if (scanner->conts) {
243 				NODE node2 = 0;
244 				for (node2 = nchild(node); node2; node2 = nsibling(node2)) {
245 					STRING tag2 = ntag(node2);
246 					if (tag2 && (eqstr(tag2, "CONC") || eqstr(tag2, "CONT"))) {
247 						STRING val2 = nval(node2);
248 						if (val2 && scanner_does_pattern_match(scanner, val2)) {
249 							CNSTRING key = nzkey(rec);
250 							scanner_add_result(scanner, key);
251 							return;
252 						}
253 					} else {
254 						break;
255 					}
256 				}
257 			}
258 		}
259 	}
260 }
261 /*==============================
262  * init_scan_pattern -- Initialize scan pattern fields
263  *============================*/
264 static void
scanner_init(SCANNER * scanner,INT scantype,CNSTRING statusmsg)265 scanner_init (SCANNER * scanner, INT scantype, CNSTRING statusmsg)
266 {
267 	scanner->scantype = scantype;
268 	scanner->seq = create_indiseq_null();
269 	strcpy(scanner->pattern, "");
270 	scanner->statusmsg = statusmsg;
271 	scanner->field = NULL;
272 	scanner->conts = FALSE;
273 }
274 /*==============================
275  * free_scanner_and_return_seq -- Free scanner data, except return result sequence
276  *============================*/
277 static INDISEQ
scanner_free_and_return_seq(SCANNER * scanner)278 scanner_free_and_return_seq (SCANNER * scanner)
279 {
280 	strfree(&scanner->field);
281 	return scanner->seq;
282 }
283 /*=================================================
284  * set_pattern -- Store scanner pattern (or return FALSE if invalid)
285  *===============================================*/
286 static BOOLEAN
scanner_set_pattern(SCANNER * scanner,STRING str)287 scanner_set_pattern (SCANNER * scanner, STRING str)
288 {
289 	INT i;
290 	/* spaces don't make sense in a name fragment */
291 	if (scanner->scantype == SCAN_NAME_FRAG) {
292 		for (i=0; str[i]; i++)
293 			if (str[i] == ' ')
294 				return FALSE;
295 	}
296 
297 	if (!fpattern_isvalid(str))
298 		return FALSE;
299 
300 	if (strlen(str) > sizeof(scanner->pattern)-1)
301 		return FALSE;
302 
303 	strcpy(scanner->pattern, str);
304 
305 	return TRUE;
306 }
307 /*=================================================
308  * scanner_set_field -- Search specified field
309  *===============================================*/
310 static void
scanner_set_field(SCANNER * scanner,STRING field)311 scanner_set_field (SCANNER * scanner, STRING field)
312 {
313 	strupdate(&scanner->field, field);
314 }
315 
316 /*=================================================
317  * scanner_scan_titles -- Search titles (& CONT & CONC children)
318  *===============================================*/
319 static void
scanner_scan_titles(SCANNER * scanner)320 scanner_scan_titles (SCANNER * scanner)
321 {
322 	strupdate(&scanner->field, "TITL");
323 	scanner->conts = TRUE;
324 }
325 /*=============================================
326  * scanner_does_pattern_match -- Compare a name to a pattern
327  *===========================================*/
328 static BOOLEAN
scanner_does_pattern_match(SCANNER * scanner,CNSTRING text)329 scanner_does_pattern_match (SCANNER *scanner, CNSTRING text)
330 {
331 	return (fpattern_matchn(scanner->pattern, text));
332 }
333 /*=============================================
334  * scanner_add_result -- Add a hit to the result list
335  *===========================================*/
336 static void
scanner_add_result(SCANNER * scanner,CNSTRING key)337 scanner_add_result (SCANNER * scanner, CNSTRING key)
338 {
339 	/* if we pass in name, append_indiseq won't check for dups */
340 	append_indiseq_null(scanner->seq, strsave(key), NULL, FALSE, TRUE);
341 }
342 /*===========================================
343  * ns_callback -- callback for name traversal
344  *=========================================*/
345 static BOOLEAN
ns_callback(CNSTRING key,CNSTRING name,BOOLEAN newset,void * param)346 ns_callback (CNSTRING key, CNSTRING name, BOOLEAN newset, void *param)
347 {
348 	INT len, ind;
349 	STRING piece;
350 	SCANNER * scanner = (SCANNER *)param;
351 	newset=newset; /* unused */
352 	if (scanner->scantype == SCAN_NAME_FULL) {
353 		if (scanner_does_pattern_match(scanner, name)) {
354 			scanner_add_result(scanner, key);
355 		}
356 	} else {
357 		/* SCAN_NAME_FRAG */
358 		LIST list = name_to_list(name, &len, &ind);
359 		FORLIST(list, el)
360 			piece = (STRING)el;
361 			if (scanner_does_pattern_match(scanner, piece)) {
362 				scanner_add_result(scanner, key);
363 				STOPLIST
364 				break;
365 			}
366 		ENDLIST
367 		destroy_list(list);
368 	}
369 	return TRUE;
370 }
371 /*===========================================
372  * rs_callback -- callback for refn traversal
373  *=========================================*/
374 static BOOLEAN
rs_callback(CNSTRING key,CNSTRING refn,BOOLEAN newset,void * param)375 rs_callback (CNSTRING key, CNSTRING refn, BOOLEAN newset, void *param)
376 {
377 	SCANNER * scanner = (SCANNER *)param;
378 	ASSERT(scanner->scantype == SCAN_REFN);
379 	newset=newset; /* unused */
380 
381 	if (scanner_does_pattern_match(scanner, refn)) {
382 		scanner_add_result(scanner, key);
383 	}
384 	return TRUE;
385 }
386