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