1 #include <config.h>
2 
3 #include <stdio.h>
4 #include <stdlib.h>
5 #ifdef HAVE_UNISTD_H
6 #include <unistd.h>
7 #endif
8 #include <string.h>
9 #include <ctype.h>
10 
11 #ifdef HAVE_DIRENT_H
12 # include <dirent.h>
13 #else
14 # define dirent direct
15 # ifdef HAVE_SYS_NDIR_H
16 #  include <sys/ndir.h>
17 # endif
18 # ifdef HAVE_SYS_DIR_H
19 #  include <sys/dir.h>
20 # endif
21 #endif
22 
23 #ifdef HAVE_LIMITS_H
24 #include <limits.h>
25 #endif
26 
27 #include "suck_config.h"
28 
29 #ifdef HAVE_REGEX_H
30 #include <regex.h>
31 #endif
32 
33 #ifdef DMALLOC
34 #include <dmalloc.h>
35 #endif
36 
37 #include "both.h"
38 #include "suck.h"
39 #include "suckutils.h"
40 #include "killfile.h"
41 #include "phrases.h"
42 #include "timer.h"
43 
44 #define GROUP_KEEP "keep"
45 #define GROUP_DELETE "delete"		/* word in param file parsed for on group line to signify keep or delete */
46 #define NEWSGROUP_HEADER "Newsgroups: "	/* header for line to match group files to */
47 
48 /* local function prototypes */
49 int get_chunk_mem(PMaster, unsigned long *len, int which, char **buf);
50 int check_newsgroups(char *, const char *);
51 void free_node(OneKill);
52 int parse_a_file(const char *, const char *, POneKill, int, int, int);
53 int pass_two(PKillStruct, int, const char *fname);
54 int check_a_group(PMaster, POneKill, char *, char **);
55 void print_debug(PKillStruct, const char *);
56 void debug_one_kill(POneKill);
57 void add_to_linkedlist(pmy_regex *, pmy_regex);
58 pmy_regex regex_scan(char *, char, int, int, char);
59 int regex_check(char *, pmy_regex, int);
60 
61 void initialize_one_kill(POneKill);
62 #ifdef HAVE_REGEX_H
63 const char regex_chars[] = "*[]()^$\\?.";  /* characters which make it a regex */
64 #endif
65 
66 void display_skiparray(unsigned char [SIZEOF_SKIPARRAY]);
67 int find_string(int, pmy_regex, char *);
68 
69 /*-------------------------------------------------------------------------*/
70 /* the enum must match the phrases in suckengl.c  killf_reasons[] */
71 enum { REASON_NONE, REASON_TOOMANYLINES, REASON_NOTENUFLINES, REASON_NRGRPS, REASON_NOKEEP, REASON_TIE, REASON_HEADER,  \
72        REASON_BODY, REASON_BODYBIG, REASON_BODYSMALL, REASON_NRXREF };
73 enum { CHECK_EXACT, CHECK_CHECK };
74 enum { CHUNK_HEADER, CHUNK_BODY };
75 
76 const struct {
77 	int len;
78 	const char *name;
79 	int headerlen;
80 	const char *header;
81 } Params[] = {
82 	{8, "HILINES=", 7, "Lines: " },
83 	{9, "LOWLINES=", 7, "Lines: "},
84 	{7, "NRGRPS=", 12, "Newsgroups: "},
85 	{6, "GROUP=", 0, ""},
86 	{6, "QUOTE=", 0, ""},
87 	{8, "PROGRAM=", 0, ""},
88 	{7, "HEADER:", 0, ""},
89 	{5, "BODY:", 0, ""},
90 	{9, "BODYSIZE>", 0, ""},
91 	{9, "BODYSIZE<", 0, ""},
92 	{21, "GROUP_OVERRIDE_MASTER", 0, ""},	/* string in masterfile to signify that group files override the master */
93 	{17, "TIEBREAKER_DELETE", 0, ""},	/* what to do if match multi groups override default of keep */
94 	{18, "USE_EXTENDED_REGEX", 0, ""}, /* do we use extended regular expressions */
95 	{10, "NON_REGEX=", 0, ""},
96 	{5,  "PERL=", 0, ""},
97 	{14, "XOVER_LOG_LONG", 0, ""}, /* make Xover killlog look like regular kill lo)*/
98 	{7,  "NRXREF=", 6, "Xref: "},
99 };
100 
101 
102 enum { PARAM_HILINE, PARAM_LOWLINE, PARAM_NRGRPS, PARAM_GROUP, PARAM_QUOTE, PARAM_PROGRAM,PARAM_HDRSCAN,
103 	PARAM_BODY, PARAM_BODYBIG, PARAM_BODYSMALL, PARAM_GRPOVERRIDE, PARAM_TIEDELETE, PARAM_E_REGEX,
104 	PARAM_NONREGEX, PARAM_PERL, PARAM_XOVER_LOG_LONG, PARAM_NRXREF};
105 #define NR_PARAMS ((int) (sizeof(Params)/sizeof(Params[0])))
106 
107 
108 /*--------------------------------------------------------------------------*/
parse_killfile(int which,int logfile_yn,int debug,int ignore_postfix)109 PKillStruct parse_killfile(int which, int logfile_yn, int debug, int ignore_postfix) {
110 	FILE *fptr;
111 	char buf[MAXLINLEN+1];
112 	int i, doprg = FALSE, mastergrp = RETVAL_OK, retval = TRUE;
113 	const char *filename;
114 #ifdef PERL_EMBED
115 	int doperl = FALSE;
116 #endif
117 	KillStruct *Masterkill = NULL;
118 
119 	/* kill file is going to get three passes, 1st one to count how many group files to process */
120 	/* so can allocate memory for all the group stuff. */
121 	/* also check for group override of masterkillfile */
122 	/* and process PROGRAM line if it exists.  If we have one we don't do anything else */
123 	/* 2nd pass will be to actually process the group files */
124 	/* 3rd pass will be to process the master delete stuff */
125 
126 	/* first malloc our master structure */
127 	if((Masterkill = malloc(sizeof(KillStruct))) == NULL) {
128 		error_log(ERRLOG_REPORT, killf_phrases[1], NULL);
129 		retval = FALSE;
130 	}
131 	else {
132 		/* now initialize everything */
133 		Masterkill->logfp = NULL;
134 		Masterkill->grp_override = FALSE;
135 		Masterkill->tie_delete = FALSE;
136 		Masterkill->totgrps = 0;
137 		Masterkill->grps = NULL;
138 		Masterkill->killfunc = chk_msg_kill;
139 		Masterkill->pbody= NULL;
140 		Masterkill->bodylen = 0;
141 		Masterkill->use_extended_regex = FALSE;
142 		Masterkill->xover_log_long = FALSE;
143 /* initialize the master struct */
144 		Masterkill->child.Stdin = Masterkill->child.Stdout = -1;
145 		Masterkill->child.Pid = -1;
146 		initialize_one_kill(&(Masterkill->master));
147 
148 #ifdef PERL_EMBED
149 		Masterkill->perl_int = NULL;
150 #endif
151 
152 		Masterkill->logyn = logfile_yn;
153 		/* which option to we use in call to full_path() ?  Do we use the postfix or not? */
154 		Masterkill->ignore_postfix = ( ignore_postfix == TRUE) ? FP_GET_NOPOSTFIX : FP_GET ;
155 
156 		filename = ( which == KILL_XOVER ) ? N_XOVER : N_KILLFILE;
157 
158 		if(debug == TRUE) {
159 			do_debug("Trying to read killfile: %s\n", full_path(Masterkill->ignore_postfix, FP_DATADIR, filename));
160 		}
161 
162 		/* FIRST PASS THRU MASTER KILLFILE - look for group delete/keeps and count em  and check for PROGRAM call*/
163 		if((fptr = fopen(full_path(Masterkill->ignore_postfix, FP_DATADIR, filename), "r")) == NULL) {
164 			/* this is not really an error, so don't report it as such */
165 			retval = FALSE;
166 		}
167 		else {
168 			if(debug == TRUE) {
169 				do_debug("Pass 1 & 2 kill file: %s\n", full_path(Masterkill->ignore_postfix, FP_DATADIR, filename));
170 			}
171 
172 			while(fgets(buf, MAXLINLEN, fptr) != NULL && doprg == FALSE) {
173 				/* nuke nl, so file names are correct, etc */
174 				i = strlen(buf);
175 				if(buf[i-1] == '\n') {
176 					buf[i-1] = '\0';
177 				}
178 				if(debug ==  TRUE) {
179 					do_debug("Read kill file line: %s\n", buf);
180 				}
181 				if(strncmp(buf, Params[PARAM_GROUP].name, (size_t) Params[PARAM_GROUP].len) == 0) {
182 					Masterkill->totgrps++;	/* how many group files must we process */
183 				}
184 				if(strncmp(buf, Params[PARAM_PROGRAM].name, (size_t) Params[PARAM_PROGRAM].len) == 0) {
185 #ifdef PERL_EMBED
186 					if(Masterkill->perl_int == NULL) {
187 #endif
188 						doprg = killprg_forkit(Masterkill, &buf[Params[PARAM_PROGRAM].len], debug, which);
189 #ifdef PERL_EMBED
190 					}
191 #endif
192 				}
193 				if(strncmp(buf, Params[PARAM_GRPOVERRIDE].name, (size_t) Params[PARAM_GRPOVERRIDE].len) == 0) {
194 					Masterkill->grp_override = TRUE;
195 				}
196 				if(strncmp(buf, Params[PARAM_TIEDELETE].name, (size_t) Params[PARAM_TIEDELETE].len) == 0) {
197 					Masterkill->tie_delete = TRUE;
198 				}
199 				if(strncmp(buf, Params[PARAM_E_REGEX].name, (size_t) Params[PARAM_E_REGEX].len) == 0) {
200 					Masterkill->use_extended_regex = TRUE;
201 				}
202 				if(strncmp(buf, Params[PARAM_XOVER_LOG_LONG].name, (size_t) Params[PARAM_XOVER_LOG_LONG].len) == 0) {
203 					Masterkill->xover_log_long = TRUE;
204 				}
205 
206 #ifdef PERL_EMBED
207 				if(strncmp(buf, Params[PARAM_PERL].name, (size_t) Params[PARAM_PERL].len) == 0) {
208 					if(doprg == FALSE) {
209 						doperl = killperl_setup(Masterkill, &buf[Params[PARAM_PERL].len], debug, which);
210 					}
211 				}
212 #endif
213 			}
214 			(void) fclose(fptr);
215 #ifndef PERL_EMBED
216 			if(doprg == TRUE) {
217 #else
218 			if(doprg == TRUE || Masterkill->perl_int != NULL) {
219 #endif
220 				Masterkill->totgrps = 0;
221 			}
222 			else {
223 				/* SECOND PASS - call routine */
224 				if(Masterkill->totgrps > 0) {
225 					if(pass_two(Masterkill, debug, filename) == RETVAL_ERROR) {
226 						retval = FALSE;
227 					}
228 				}
229 				/* THIRD PASS - process master delete stuff */
230 				if(retval != FALSE && (mastergrp = parse_a_file(filename, "Master", &(Masterkill->master), debug, Masterkill->ignore_postfix, Masterkill->use_extended_regex)) == RETVAL_ERROR) {
231 					retval = FALSE;
232 				}
233 			}
234 		}
235 
236 		/* do we have an error OR do we have empty killfiles */
237 		if((retval == FALSE) || (mastergrp == RETVAL_EMPTYKILL && Masterkill->totgrps == 0 && doprg == FALSE)) {
238 #ifdef PERL_EMBED
239 			if(Masterkill->perl_int == NULL) {
240 #endif
241 				free_killfile(Masterkill);	/* just in case any memory got allocated */
242 				Masterkill = NULL;
243 #ifdef PERL_EMBED
244 			}
245 #endif
246 		}
247 		else if(debug == TRUE) {
248 			print_debug(Masterkill, filename);
249 		}
250 	}
251 
252 	return Masterkill;
253 }
254 /*-------------------------------------------------------------------------------------------*/
255 int pass_two(PKillStruct killp, int debug, const char *filename ) {
256 
257 	int retval = RETVAL_OK;
258 	FILE *fptr;
259 	char buf[MAXLINLEN];
260 	int grpon = 0;
261 	size_t i;
262 	char *grpname, *grpfile, *delkeep;
263 
264 	grpname = grpfile = delkeep = NULL;
265 
266 	/* SECOND PASS - now that we know how many, we can allocate the space for em */
267 	/* and then have parse_a_file read em in. */
268 	if((killp->grps = calloc((size_t) killp->totgrps, sizeof(Group))) == NULL) {
269 		retval = RETVAL_ERROR;
270 		error_log(ERRLOG_REPORT, killf_phrases[1], NULL);
271 	}
272 	else if((fptr = fopen(full_path(killp->ignore_postfix, FP_DATADIR, filename), "r")) == NULL) {
273 		MyPerror(full_path(killp->ignore_postfix, FP_DATADIR, filename));
274 		retval = RETVAL_ERROR;
275 	}
276 	else {
277 		while(retval == RETVAL_OK && fgets(buf, MAXLINLEN, fptr) != NULL) {
278 			if(strncmp(buf, Params[PARAM_GROUP].name, (size_t) Params[PARAM_GROUP].len) == 0 ) {
279 
280 				/* now parse the line for the 3 required elements */
281 				/* keep/delete group_name filename */
282 				delkeep = &buf[Params[PARAM_GROUP].len];
283 				if(strncmp(delkeep, GROUP_KEEP, (size_t) strlen(GROUP_KEEP)) == 0) {
284 					killp->grps[grpon].delkeep = DELKEEP_KEEP;
285 				}
286 				else if(strncmp(delkeep, GROUP_DELETE, (size_t) strlen(GROUP_DELETE)) == 0) {
287 					killp->grps[grpon].delkeep = DELKEEP_DELETE;
288 				}
289 				else {
290 					retval = RETVAL_ERROR;
291 				}
292 				if(retval == RETVAL_OK) {
293 					grpname = strchr(delkeep, ' ');	/* find the space */
294 					if(grpname == NULL) {
295 						retval = RETVAL_ERROR;
296 					}
297 					else {
298 						++grpname;	/* move past space */
299 						grpfile = strchr(grpname, ' ');
300 						if(grpfile == NULL) {
301 							retval = RETVAL_ERROR;
302 						}
303 						else {
304 							*grpfile = '\0';	/* truncate the group name for easier copying later */
305 							++grpfile;
306 
307 							/* nuke newline */
308 							i = strlen(grpfile) - 1;
309 							if(grpfile[i] == '\n') {
310 								grpfile[i] = '\0';
311 							}
312 						}
313 					}
314 				}
315 				if(retval == RETVAL_ERROR) {
316 					error_log(ERRLOG_REPORT, killf_phrases[2], buf, NULL);
317 				}
318 				else {	/* have all three params, put them in place and parse the file */
319 					/* +1 for newline */
320 					if((killp->grps[grpon].group = malloc(strlen(grpname)+1)) == NULL) {
321 						error_log(ERRLOG_REPORT, killf_phrases[0], NULL);
322 						retval = RETVAL_ERROR;
323 					}
324 					else {
325 						strcpy(killp->grps[grpon].group, grpname);
326 						/* get, ignoring postfix, so use absolute filename on group line */
327 						if(parse_a_file(grpfile, grpname, &(killp->grps[grpon].match), debug,FP_GET_NOPOSTFIX,killp->use_extended_regex) != RETVAL_OK) {
328 							/* whoops couldn't open file, ignore */
329 							free(killp->grps[grpon].group);
330 							grpon--;  /* so that we reuse this grp entry */
331 							killp->totgrps--;
332 						}
333 
334 					}
335 				}
336 				grpon++;	/* finished with this group */
337 			}
338 		}
339 		(void) fclose(fptr);
340 	}
341 
342 	return retval;
343 }
344 /*--------------------------------------------------------------------------*/
345 void free_killfile(PKillStruct master) {
346 
347 	int i;
348 
349 	if(master != NULL) {
350 		/* first kill off killprg if its there */
351 		if(master->killfunc==chk_msg_kill_fork || master->child.Pid != -1) {
352 			killprg_closeit(master);
353 		}
354 #ifdef PERL_EMBED
355 		if(master->perl_int != NULL) {
356 			killperl_done(master);
357 		}
358 #endif
359 		/* close off log, if opened */
360 		if(master->logfp != NULL) {
361 			fclose(master->logfp);
362 		}
363 		free_node(master->master);
364 		if(master->totgrps > 0) {
365 			for(i=0;i<master->totgrps;i++) {
366 				free_node(master->grps[i].match);
367 				free(master->grps[i].group);
368 			}
369 			free(master->grps);
370 		}
371 
372 		free(master);
373 	}
374 }
375 /*--------------------------------------------------------------------*/
376 void free_node(OneKill node) {
377 
378 	pmy_regex curr, next;
379 
380 		curr = node.list;
381 
382 		if (node.header != NULL) {
383 #ifdef HAVE_REGEX_H
384 			if(node.header->ptrs != NULL) {
385 				regfree(node.header->ptrs);
386 			}
387 #endif
388 			if(node.header->header != NULL) {
389 				free(node.header->header);
390 			}
391 			if(node.header->string != NULL) {
392 				free(node.header->string);
393 			}
394 		}
395 		while(curr != NULL) {
396 #ifdef HAVE_REGEX_H
397 			if(curr->ptrs != NULL) {
398 				regfree(curr->ptrs);
399 			}
400 #endif
401 			if(curr->header != NULL) {
402 				free(curr->header);
403 			}
404 			if(curr->string != NULL) {
405 				free(curr->string);
406 			}
407 			next = curr->next;
408 			free(curr);
409 			curr = next;
410 		}
411 
412 }
413 /*--------------------------------------------------------------------------*/
414 int get_one_article_kill(PMaster master, int logcount, long itemon) {
415 
416 	char buf[MAXLINLEN+1], *inbuf;
417 	const char *tname;
418 	char fname[PATH_MAX+1];
419 	int retval, x;
420 	unsigned long len;
421 	FILE *fptr;
422 	PKillStruct killp;
423 
424 	killp = master->killp;
425 
426 	retval = RETVAL_OK;
427 
428 	killp->pbody = NULL;	/* since we haven't downloaded anything yet for this article */
429 	killp->bodylen = 0;
430 
431 	retval =get_chunk_mem(master, &len, CHUNK_HEADER, &inbuf);
432 
433 	/* the killfunc pointer points to either chk_msg_kill(), chk_msg_kill_fork(), or chk_msg_kill_perl() */
434 	/* do we have to download this sucker, is it mandatory? */
435 	if(retval == RETVAL_OK && ((master->curr)->mandatory == MANDATORY_YES || (*killp->killfunc)(master, killp, inbuf, len) == FALSE)) {
436 		if(master->MultiFile == TRUE) {
437 				/* open file */
438 				/* file name will be ####-#### ex 001-166 (nron,total) */
439 			sprintf(buf,"%0*ld-%d", logcount, itemon, master->nritems);
440 
441 				/* the strcpy to avoid wiping out fname in second call to full_path */
442 			strcpy(fname, full_path(FP_GET, FP_MSGDIR, buf));
443 			strcat(buf, N_TMP_EXTENSION);	/* add temp file extension */
444 			tname = full_path(FP_GET, FP_TMPDIR, buf);	/* temp file name */
445 			if(master->debug == TRUE) {
446 				do_debug("File name = \"%s\" temp = \"%s\"", fname, tname);
447 			}
448 			if((fptr = fopen(tname, "w")) == NULL) {
449 				MyPerror(tname);
450 				retval = RETVAL_ERROR;
451 			}
452 			else {
453 				/* write the header */
454 				x = fwrite(inbuf,sizeof(inbuf[0]),len, fptr);
455 				fputs("\n", fptr);	/* needed */
456 				if(x != len) {
457 					retval = RETVAL_ERROR;
458 					error_log(ERRLOG_REPORT, killf_phrases[9], NULL);
459 				}
460 				if(retval == RETVAL_OK && master->header_only == FALSE) {
461 					/* have we already downloaded the body? */
462 					if(killp->pbody != NULL) {
463 						inbuf = killp->pbody;
464 						len = killp->bodylen;
465 					}
466 					else {
467 						retval = get_chunk_mem(master, &len, CHUNK_BODY, &inbuf);
468 					}
469 					if(retval == RETVAL_OK) {
470 						x = fwrite(inbuf, sizeof(inbuf[0]), len, fptr);
471 						if(x != len) {
472 							retval = RETVAL_ERROR;
473 							error_log(ERRLOG_REPORT, killf_phrases[9], NULL);
474 						}
475 					}
476 				}
477 				(void) fclose(fptr);
478 				if(retval != RETVAL_OK) {
479 					unlink(tname);
480 				}
481 				/* now rename it to the permanent file name */
482 				else {
483 					move_file(tname, fname);
484 					if((master->batch == BATCH_LIHAVE || master->batch == BATCH_INNFEED) && master->innfeed != NULL) {
485 						/* write path name and msgid to file */
486 						fprintf(master->innfeed, "%s %s\n", fname, (master->curr)->msgnr);
487 						fflush(master->innfeed); /* so it gets written sooner */
488 					}
489 				}
490 			}
491 		}
492 		else {
493 			fputs(inbuf, stdout);
494 			fputs("\n", stdout);
495 			if(master->header_only == FALSE) {
496 				retval = get_chunk_mem(master, &len, CHUNK_BODY, &inbuf);
497 				if(retval == RETVAL_OK) {
498 					fwrite(inbuf, sizeof(inbuf[0]), len, stdout);
499 				}
500 			}
501                         /* this is needed as a separator in stdout version */
502 			fputs(".\n", stdout);
503 		}
504 		if(retval == RETVAL_OK) {
505 			master->nrgot++;
506 		}
507 	}
508 	if(retval == RETVAL_UNEXPECTEDANS) {
509 		retval = RETVAL_OK; /* so don't abort */
510 	}
511 
512 	return retval;
513 }
514 /*---------------------------------------------------------------*/
515 /* this routine gets the header or body into memory, keeping separate buffers for each */
516 int get_chunk_mem(PMaster master, unsigned long *size, int which, char **retbuf) {
517 
518 
519 	static char *header_buf = NULL;
520 	static char *body_buf = NULL;
521 	static int header_size = 8192;
522 	static unsigned long body_size = KILL_BODY_BUF_SIZE;
523 
524 	int done, partial, len, i, retval;
525 	char *inbuf, *newbuf, *buf;
526 	const char *cmd;
527 	unsigned long temp, bufsize, currbuf; /* bufsize = alloced memory, currbuf = what retrieved so far */
528 
529 
530 	done = FALSE;
531 	partial = FALSE;
532 	currbuf = 0;
533 	retval = RETVAL_OK;
534 
535 	if(which == CHUNK_HEADER) {
536 		buf = header_buf;
537 		bufsize = header_size;
538 	}
539 	else {
540 		buf = body_buf;
541 		bufsize = body_size;
542 	}
543 
544 	if(buf == NULL) {
545 		if((buf=malloc((size_t) bufsize)) == NULL) {
546 			error_log(ERRLOG_REPORT, killf_phrases[0], NULL);
547 			retval = RETVAL_ERROR;
548 		}
549 	}
550 
551 	if(buf != NULL) {
552 		/* build command */
553 		if(which == CHUNK_HEADER) {
554 			cmd = build_command(master, "head", master->curr);
555 			i = 221;
556 		}
557 		else {
558 			cmd = build_command(master, "body", master->curr);
559 			i = 222;
560 		}
561 		if((retval = send_command(master, cmd, NULL, i)) != RETVAL_OK) {
562 			free(buf);
563 			buf = NULL;
564 		}
565 	}
566 	while(buf != NULL && done == FALSE) {
567 		len=sgetline(master->sockfd, &inbuf, master->do_ssl, master->ssl_struct);
568 		(void) TimerFunc(TIMER_ADDBYTES, len, NULL);
569 		if(len < 0) {
570 			free(buf);
571 			buf = NULL;
572 			done = TRUE;
573 			retval = RETVAL_ERROR;
574 		}
575 		else if(partial == FALSE && inbuf[0] == '.') {
576 			if(len == 2 && inbuf[1] == '\n') {
577 				done = TRUE;
578 			}
579 			else {
580 				/* handle double dots IAW RFC977 2.4.1*/
581 				inbuf++;	/* move past first dot */
582 				len--;
583 			}
584 		}
585 		if(done == FALSE) {
586 			while((len+currbuf) > bufsize && buf != NULL) {
587 				/* buffer not big enough realloc */
588 				/* how much do we increase buf */
589 				/* we do this test so in case KILL_CHUNK_BUF_INCREASE < len, we */
590 				/* don't get a buffer overflow */
591 				temp = (len > KILL_CHUNK_BUF_INCREASE) ? len : KILL_CHUNK_BUF_INCREASE;
592 				if(master->debug == TRUE) {
593 					do_debug("Re-allocing buffer from %lu to %lu\n", bufsize, bufsize+temp);
594 				}
595 				bufsize += temp;
596 				if((newbuf = realloc(buf, (size_t) bufsize)) == NULL) {
597 					free(buf);
598 					buf = NULL;
599 					currbuf = 0;
600 					error_log(ERRLOG_REPORT, killf_phrases[0], NULL);
601 					retval = RETVAL_ERROR;
602 				}
603 				else {
604 					buf = newbuf;
605 				}
606 			}
607 			if(buf != NULL) {
608 				/* put string in buffer, use memmove in case of nulls*/
609 				memmove(buf+currbuf, inbuf, len);
610 				currbuf += len;
611 				partial= (len==MAXLINLEN&&inbuf[len-1]!='\n') ? TRUE : FALSE;
612 			}
613 		}
614 	}
615 	/* now save the values for next time */
616 	if(which == CHUNK_HEADER) {
617 		header_buf = buf;
618 		header_size = bufsize;
619 	}
620 	else {
621 		body_buf = buf;
622 		body_size = bufsize;
623 	}
624 	/* return the length */
625 	*size = currbuf;
626 
627 	/* make sure buf is NULL terminated */
628 	/* this is needed so logging is correct */
629 	/* since using memmove() above, we don't copy the ending NULL */
630 	if(buf != NULL) {
631 		buf[currbuf] = '\0';
632 	}
633 
634 	*retbuf = buf;
635 	return retval;
636 }
637 /*-------------------------------------------------------------------------*/
638 /* chk_msg_kill - return TRUE if kill article, FALSE if keep               */
639 /* if kill article, add it to killlog 					   */
640 int chk_msg_kill(PMaster master, PKillStruct killp, char *headerbuf, int buflen) {
641 
642 	int killyn, i, del, keep, match, masterkill;
643 	const char *group = "Master";
644 	char *why, *goodwhy;
645 
646 	goodwhy = why = killf_reasons[REASON_NONE];
647 	killyn = FALSE;
648 
649 	/* first check against master delete */
650 	masterkill = check_a_group(master, &(killp->master), headerbuf, &why);
651 	if(masterkill == TRUE && killp->grp_override == FALSE) {
652 		killyn = masterkill;
653 	}
654 	else {
655 		/* okay now have to parse group line */
656 		/* then check to see if I have group keep/deletes for each group */
657 		/* default actions */
658  		keep = FALSE;
659 		del = FALSE;
660 		for(i=0;i<killp->totgrps;i++) {
661 			if(check_newsgroups(headerbuf, killp->grps[i].group) == TRUE) {
662 				/* bingo this article matches one of our group check it */
663 				match = check_a_group(master, &(killp->grps[i].match), headerbuf, &why);
664 				if(killp->grps[i].delkeep == DELKEEP_KEEP) {
665 					/* matched keep group */
666 					if(match == TRUE) {
667 						keep = TRUE;
668 					}
669 					else {
670 						del = TRUE;
671 						group = killp->grps[i].group;
672 						goodwhy = killf_reasons[REASON_NOKEEP];
673 					}
674 				}
675 				else {
676 					if(match == TRUE) {
677 						del = TRUE;
678 						goodwhy = why;
679 						group = killp->grps[i].group;
680 					}
681 					else {
682 						keep = TRUE;
683 					}
684 				}
685 			}
686 		}
687 		/* now determine if we kill or keep this sucker */
688 		if(keep == FALSE && del == FALSE) {
689 			/* no group matches, do what masterkill says to do */
690 			killyn = masterkill;
691 		}
692 		else if(keep != del) {
693 			/* only matched one group, figure out which */
694 			killyn = ( del == TRUE) ? TRUE : FALSE;
695 			why = goodwhy;
696 		}
697 		else {
698 			/* matched both, use TIEBREAKER */
699 			why = killf_reasons[REASON_TIE];
700 			killyn = killp->tie_delete;
701 		}
702 	}
703 	if(master->debug == TRUE && killyn == TRUE) {
704 		do_debug("killing: %s: %s: %s", group, why, headerbuf);
705 	}
706 	if(killyn == TRUE && killp->logyn != KILL_LOG_NONE) {
707 		/* log it */
708 		/* only open this sucker once */
709 		if(killp->logfp == NULL) {
710 			if((killp->logfp = fopen(full_path(FP_GET, FP_TMPDIR, master->kill_log_name), "a")) == NULL) {
711 				MyPerror(killf_phrases[3]);
712 			}
713 		}
714 		if(killp->logfp != NULL) {
715 			/* first print our one-line reason */
716 			print_phrases(killp->logfp,killf_phrases[4], group ,why, (master->curr)->msgnr, NULL);
717 
718 			if(killp->logyn == KILL_LOG_LONG) {
719 				/* print the header as well */
720 				/* the nl so I get a blank line between em */
721 				print_phrases(killp->logfp, "%v1%\n", headerbuf, NULL);
722 			}
723 
724 		}
725 	}
726 	return killyn;
727 }
728 /*-----------------------------------------------------------------------*/
729 int check_a_group(PMaster master, POneKill killp, char *headerbuf, char **why) {
730 
731 	int i, match = FALSE;
732 	char *startline, *tptr;
733 	pmy_regex curr;
734 	static char reason[MAXLINLEN];
735 	PKillStruct temp;
736 
737 	/* check hilines first */
738 	if(killp->hilines > 0) {
739 		if((startline = strstr(headerbuf, Params[PARAM_HILINE].header)) != NULL)  {
740 			i = 0;	/* just in case */
741 			sscanf(startline+Params[PARAM_HILINE].headerlen, "%d", &i);
742 			if(killp->hilines < i) {
743 				/* JACKPOT */
744 				match = TRUE;
745 				*why = killf_reasons[REASON_TOOMANYLINES];
746 			}
747 		}
748 
749 	}
750 	/* now check low lines */
751 	if(match == FALSE && killp->lowlines > 0) {
752 		if((startline = strstr(headerbuf, Params[PARAM_LOWLINE].header)) != NULL)  {
753 			i = 0;	/* just in case */
754 			sscanf(startline+Params[PARAM_LOWLINE].headerlen, "%d", &i);
755 			if(i < killp->lowlines) {
756 				/* JACKPOT */
757 				match = TRUE;
758 				*why = killf_reasons[REASON_NOTENUFLINES];
759 			}
760 		}
761 
762 	}
763 	/* now check nrgrps */
764 	if(match == FALSE && killp->maxgrps > 0) {
765 		if((startline = strstr(headerbuf, Params[PARAM_NRGRPS].header)) != NULL) {
766 			/* count the nr of commas in the group line */
767 			i = 1;	/* have at least one group */
768 			tptr = startline;
769 			while(i <= killp->maxgrps && *tptr != '\n' && *tptr != '\0' ) {
770 				/* some news server use space vice comma for separator */
771 				if(*tptr == COMMA || *tptr == SPACE ) {
772 					i++;
773 				}
774 				tptr++;
775 			}
776 			if(i > killp->maxgrps) {
777 				match = TRUE;
778 				*why = killf_reasons[REASON_NRGRPS];
779 			}
780 		}
781 	}
782 	/* now check nrxref */
783 	if(match == FALSE && killp->maxxref > 0) {
784 		if((startline = strstr(headerbuf, Params[PARAM_NRXREF].header)) != NULL) {
785 			/* count the nr of colons in  line, they separate the group/article nr */
786 			i = 0;
787 			tptr = startline;
788 			while(i <= killp->maxxref && *tptr != '\n' && *tptr != '\0' ) {
789 				if(*tptr == COLON) {
790 					i++;
791 				}
792 				tptr++;
793 			}
794 			if(i > killp->maxxref) {
795 				match = TRUE;
796 				*why = killf_reasons[REASON_NRXREF];
797 			}
798 		}
799 	}
800 	curr = killp->list;
801 
802 	while(match == FALSE && curr != NULL) {
803 		if(regex_check(headerbuf, curr, master->debug) == TRUE) {
804 			match = TRUE;
805 			sprintf(reason, "%s %s %s ", killf_reasons[REASON_HEADER], curr->header, curr->string);
806 			*why = reason;
807 		}
808 		curr = curr->next;
809 	}
810 	curr = killp->header;
811 	while(match == FALSE && curr != NULL) {
812 		/* scan the entire header */
813 		if(regex_block(headerbuf, curr, master->debug) == TRUE) {
814 			match  = TRUE;
815 			sprintf(reason,"%s %s", killf_reasons[REASON_HEADER], curr->string);
816 			*why = reason;
817 		}
818 		curr = curr->next;
819 	}
820 	if(match == FALSE && (killp->bodybig > 0 || killp->bodysmall > 0 || killp->body != NULL)) {
821 		/* may have to download the header first to */
822 		/* have a pointer to the master killstruct */
823 		temp = master->killp;
824 		if(temp->pbody == NULL) {
825 			/* have to get the body first */
826 			get_chunk_mem(master, &(temp->bodylen), CHUNK_BODY, &(temp->pbody));
827 		}
828 		if(killp->bodybig > 0 && temp->bodylen > killp->bodybig) {
829 			match = TRUE;
830 			sprintf(reason, "%s %lu", killf_reasons[REASON_BODYBIG], temp->bodylen);
831 			*why = reason;
832 		}
833 		else if(killp->bodysmall > 0 && temp->bodylen < killp->bodysmall) {
834 			match = TRUE;
835 			sprintf(reason, "%s %lu", killf_reasons[REASON_BODYSMALL], temp->bodylen);
836 			*why = reason;
837 		}
838 		else {
839 			curr = killp->body;
840 			while(match == FALSE && curr != NULL) {  /* scan the body */
841 				if(regex_block(temp->pbody, curr, master->debug) == TRUE) {
842 					match = TRUE;
843 					sprintf(reason, "%s %s", killf_reasons[REASON_BODY], curr->string);
844 					*why = reason;
845 				}
846 				curr = curr->next;
847 			}
848 		}
849 	}
850 
851 	return match;
852 }
853 /*--------------------------------------------------------------------------*/
854 int check_newsgroups(char *header, const char *whichgroup) {
855 	/* search Newsgroup headerline for whichgroup  */
856 	/* if in return TRUE else return FALSE; */
857 
858 	int match = FALSE;
859 	char *startline, *ptr;
860 	const char *ptr_group;
861 
862 	if((startline = strstr(header, NEWSGROUP_HEADER)) != NULL) {
863 		ptr = startline + strlen(NEWSGROUP_HEADER);
864 		while( match == FALSE && *ptr != '\0' && *ptr != '\n' ) {
865 			ptr_group = whichgroup;
866 			while( *ptr == *ptr_group && *ptr != '\0') {
867 				ptr++;
868 				ptr_group++;
869 			}
870 			if(*ptr_group == '*') {
871 				/* wildcard match, they match so far, so they match */
872 				match = TRUE;
873 			}
874 			else if((*ptr_group == '\0') && (*ptr == COMMA || *ptr == ' ' || *ptr == '\n' || *ptr == '\0')) {
875 				/* if we are at the end of both group names then we have a match */
876 				/* we check for space even though its not in the standard, apparently */
877 				/* some news server use it. */
878 				match = TRUE;
879 			}
880 			else {
881 				/* advance to next group on line */
882 				while(*ptr != COMMA && *ptr != '\n' && *ptr != '\0') {
883 					ptr++;
884 				}
885 				if(*ptr == COMMA) {
886 					/* advance past it */
887 					ptr++;
888 				}
889 			}
890 		}
891 	}
892 	return match;
893 }
894 /*-------------------------------------------------------------------------------*/
895 void initialize_one_kill(POneKill mykill) {
896 	/* initialize the various elements*/
897 	mykill->list = NULL;
898 	mykill->header = NULL;
899 	mykill->body = NULL;
900 	mykill->hilines = mykill->lowlines = mykill->maxgrps = mykill->maxxref = 0;
901 	mykill->bodybig = mykill->bodysmall = 0;
902 	mykill->quote = KILLFILE_QUOTE;
903 	mykill->non_regex = KILLFILE_NONREGEX;
904 
905 	return;
906 }
907 
908 /*-------------------------------------------------------------------------------*/
909 int parse_a_file(const char *fname, const char *group, POneKill mykill, int debug, int ignore_prefix, int use_e_regex) {
910 
911 	FILE *fptr;
912 	char buf[MAXLINLEN+1];
913 	int i, match;
914 	int retval = RETVAL_OK;
915 	pmy_regex curr;
916 
917 	/* first initialize the killstruct */
918 	initialize_one_kill(mykill);
919 #ifdef HAVE_REGEX_H
920 	mykill->use_extended = use_e_regex;
921 #endif
922 	/* now read in the killfile and parse it */
923 	if((fptr = fopen(full_path(ignore_prefix, FP_DATADIR, fname), "r")) == NULL) {
924 		MyPerror(full_path(ignore_prefix, FP_DATADIR, fname));
925 		retval = RETVAL_ERROR;
926 	}
927 	else {
928 		if(debug == TRUE) {
929 			do_debug("Pass 3 kill file: %s\n", full_path(ignore_prefix, FP_DATADIR, fname));
930 			do_debug("Pass 3 options: group=%s, use_extended_regex=%s, ignore_prefix=%s\n",
931 				 group, true_str(use_e_regex), true_str(ignore_prefix));
932 		}
933 		while(fgets(buf, MAXLINLEN, fptr) != NULL) {
934 			buf[MAXLINLEN] = '\0';	/* just in case */
935 
936 			i = strlen(buf);
937 			/* strip nls off so they don't get added to regex scan, etc..*/
938 			if(buf[i-1] == '\n') {
939 				buf[i-1] = '\0';
940 			}
941 			if(debug == TRUE) {
942 				do_debug("pass 3 kill file line: %s\n", buf);
943 			}
944 			if(buf[0] != KILLFILE_COMMENT_CHAR) {
945 				/* skip any comment lines */
946 				match = FALSE;
947 				for(i = 0 ; i < NR_PARAMS; i++) {
948 					if(strncmp(buf, Params[i].name, (size_t) Params[i].len) == 0) {
949 						match = TRUE; /* so don't try header field scan later */
950 						switch(i) {
951 						case PARAM_HILINE:
952 							(void) sscanf(&buf[Params[PARAM_HILINE].len], "%d", &(mykill->hilines));
953 							break;
954 						case PARAM_LOWLINE:
955 							(void) sscanf(&buf[Params[PARAM_LOWLINE].len], "%d", &(mykill->lowlines));
956 							break;
957 						case PARAM_NRGRPS:
958 							(void) sscanf(&buf[Params[PARAM_NRGRPS].len], "%d", &(mykill->maxgrps));
959 							break;
960 						case PARAM_NRXREF:
961 							(void) sscanf(&buf[Params[PARAM_NRXREF].len], "%d", &(mykill->maxxref));
962 							break;
963 						case PARAM_QUOTE:
964 							if(buf[Params[PARAM_QUOTE].len] == '\0') {
965 								error_log(ERRLOG_REPORT, "%s\n", killf_phrases[6], NULL);
966 							}
967 							else {
968 								mykill->quote = buf[Params[PARAM_QUOTE].len];
969 							}
970 							break;
971 						case PARAM_NONREGEX:
972 							if(buf[Params[PARAM_NONREGEX].len] == '\0') {
973 								error_log(ERRLOG_REPORT, "%s\n", killf_phrases[12], NULL);
974 							}
975 							else {
976 								mykill->non_regex = buf[Params[PARAM_NONREGEX].len];
977 							}
978 							break;
979 						case PARAM_HDRSCAN:
980 							if(buf[Params[PARAM_HDRSCAN].len] == '\0') {
981 								error_log(ERRLOG_REPORT, "%s\n", killf_phrases[7], NULL);
982 							}
983 							else {
984 								if((curr = regex_scan(buf, mykill->quote, debug, use_e_regex, mykill->non_regex)) != NULL) {
985 									add_to_linkedlist(&(mykill->header), curr);
986 								}
987 							}
988 							break;
989 						case PARAM_BODY:
990 							if(buf[Params[PARAM_BODY].len] == '\0') {
991 								error_log(ERRLOG_REPORT, "%s\n", killf_phrases[10], NULL);
992 							}
993 							else {
994 								if((curr = regex_scan(buf, mykill->quote, debug, use_e_regex, mykill->non_regex)) != NULL) {
995 									add_to_linkedlist(&(mykill->body), curr);
996 								}
997 							}
998 							break;
999 						case PARAM_BODYBIG:
1000 							(void) sscanf(&buf[Params[PARAM_BODYBIG].len], "%lu", &(mykill->bodybig));
1001 							break;
1002 						case PARAM_BODYSMALL:
1003 							(void) sscanf(&buf[Params[PARAM_BODYSMALL].len], "%lu", &(mykill->bodysmall));
1004 							break;
1005 						case PARAM_E_REGEX:
1006 							/* in case specified for this file only */
1007 							use_e_regex = TRUE;
1008 #ifdef HAVE_REGEX_H
1009 							mykill->use_extended = TRUE; /*  save for printing in debug */
1010 #endif
1011 							break;
1012 						default:		/* handle all other lines aka do nothing*/
1013 							break;
1014 
1015 						}
1016 					}
1017 				}
1018 				if(match == FALSE) {
1019 				/* it's a different line in the header add to linked-list */
1020 					if((curr = regex_scan(buf, mykill->quote, debug, use_e_regex, mykill->non_regex)) != NULL) {
1021 						add_to_linkedlist(&(mykill->list), curr);
1022 					}
1023 				}
1024 			} /* comment line stuff */
1025 		}
1026 		(void) fclose(fptr);
1027 		/* check for an empty killfile */
1028 		if(mykill->hilines == 0 && mykill->lowlines == 0 && mykill->maxgrps == 0 && mykill->bodybig == 0 && mykill->bodysmall == 0 && mykill->header == NULL && mykill->list == NULL && mykill->body == NULL && mykill->maxxref == 0) {
1029 			retval = RETVAL_EMPTYKILL;
1030 		}
1031 	}
1032 	return retval;
1033 }
1034 /*---------------------------------------------------------------------------------------------*/
1035 void add_to_linkedlist(pmy_regex *head_ptr, pmy_regex addon) {
1036 
1037 	/* this routine adds a pointer the end of a linked list */
1038 	pmy_regex curr;
1039 	if(*head_ptr == NULL) {
1040 		/* put on top of list */
1041 		*head_ptr = addon;
1042 	}
1043 	else {
1044 		curr = *head_ptr;
1045 		while(curr->next != NULL) {
1046 			curr = curr->next;
1047 		}
1048 		curr->next = addon;
1049 	}
1050 }
1051 /*----------------------------------------------------------------------------------*/
1052 /* scan a line and build the compiled regex pointers to call regexex() with */
1053 /* ---------------------------------------------------------------------------------*/
1054 pmy_regex regex_scan(char *linein, char quotechar, int debug, int use_e_regex, char non_regex) {
1055 
1056 	char *tptr, *startline;
1057 	int len,x,y, case_sens = FALSE, flags = FALSE;
1058 #ifdef HAVE_REGEX_H
1059 	char errmsg[256];
1060 	int i,j, useregex = TRUE;
1061 #endif
1062 
1063 	pmy_regex which = NULL;
1064 	int err = FALSE;
1065 
1066 	if(linein != NULL) {
1067 		/* first find the : in the line to skip the parameter part */
1068 		tptr = linein;
1069 		while(*tptr != '\0' && *tptr != ':') {
1070 			tptr++;
1071 		}
1072 		startline = tptr+1;
1073 		len = startline - linein;
1074 
1075 		/* do we treat this string as a non-regex?? */
1076 		if(*startline == non_regex) {
1077 			startline++;
1078 #ifdef HAVE_REGEX_H
1079 			useregex = FALSE;
1080 #endif
1081 		}
1082 		/* case sensitive search?? */
1083 		if(*startline == quotechar) {
1084 			startline++;
1085 			case_sens = TRUE;
1086 		}
1087 #ifdef HAVE_REGEX_H
1088 		if(useregex == TRUE) {
1089 			flags = REG_NOSUB;
1090 			if(case_sens == FALSE) {
1091 				flags |= REG_ICASE;
1092 			}
1093 			if(use_e_regex == TRUE) {
1094 				flags |= REG_EXTENDED;
1095 			}
1096 			useregex  = FALSE;
1097 			/* test to see whether or not we need to use regex */
1098 			for(i = 0; i < strlen(regex_chars) && useregex != TRUE; i++) {
1099 				for(j = 0 ; j < strlen(startline)&& useregex != TRUE; j++) {
1100 					if(startline[j] == regex_chars[i]) {
1101 						useregex = TRUE;
1102 					}
1103 				}
1104 			}
1105 		}
1106 
1107 #endif
1108 		/* now count each of em, so that can alloc my array of regex_t */
1109 		if(*tptr != '\0') {
1110 			/* now alloc memory */
1111 
1112 			if((which = malloc(sizeof (my_regex))) == NULL) {
1113 				error_log(ERRLOG_REPORT, killf_phrases[5], NULL);
1114 			}
1115 			else {
1116 				/* first save the string for later printing, if necessary */
1117 				if((which->string = malloc(strlen(startline)+1)) == NULL) {
1118 					err = TRUE;
1119 					error_log(ERRLOG_REPORT, killf_phrases[5], NULL);
1120 				}
1121 				else {
1122 					strcpy(which->string,startline);
1123 					which->next = NULL;	/* initialize */
1124 					which->case_sensitive = case_sens;
1125 #ifdef HAVE_REGEX_H
1126 					which->ptrs = NULL;	/* just to be on safe side */
1127 #endif
1128 					if((which->header=calloc(sizeof(char), len+1)) == NULL) {
1129 						error_log(ERRLOG_REPORT, killf_phrases[5], NULL);
1130 						err = TRUE;
1131 					}
1132 					else {
1133 						strncpy(which->header, linein, len);
1134 						which->header[len] = '\0';	/* just in case */
1135 #ifdef HAVE_REGEX_H
1136 						if(useregex == TRUE) {
1137 							if((which->ptrs = malloc(sizeof(regex_t))) == NULL) {
1138 								error_log(ERRLOG_REPORT, killf_phrases[5], NULL);
1139 							}
1140 							else {
1141 								if(debug == TRUE) {
1142 									do_debug("Regcomping -%s-\n", startline);
1143 								}
1144 								if((err = regcomp(which->ptrs, startline, flags)) != 0) {
1145 									/* whoops */
1146 									regerror(err, which->ptrs, errmsg, sizeof(errmsg));
1147 									error_log(ERRLOG_REPORT, killf_phrases[11], startline, errmsg, NULL);
1148 									err= TRUE;
1149 								}
1150 							}
1151 						}
1152 						else {
1153 #endif
1154 							/* not using regex, fill in skiparray */
1155 							/* first fill in max skip for all chars (len of string) */
1156 							/* See Algorithms/Sedgewick pg 287-289 for explanation */
1157 							y=strlen(which->string);
1158 
1159 							for(x=0;x<sizeof(which->skiparray);x++) {
1160 								which->skiparray[x] = y;
1161 							}
1162 							/* now go back and fill in those in our search string */
1163 							if(case_sens == FALSE) {
1164 								/* case-insensitive search */
1165 								for(x=0;x<y;x++) {
1166 									/* have to match both upper and lower case */
1167 									char xx;
1168 									xx = which->string[x];
1169 									which->skiparray[(unsigned char) toupper(xx)] = (y-1)-x;
1170 									which->skiparray[(unsigned char) tolower(xx)] = (y-1)-x;
1171 								}
1172 							}
1173 							else {
1174 								for(x=1;x<y;x++) {
1175 									which->skiparray[(unsigned char) which->string[x]] = (y-1)-x;
1176 								}
1177 							}
1178 
1179 
1180 #ifdef HAVE_REGEX_H
1181 						}
1182 #endif
1183 					}
1184 				}
1185 			}
1186 
1187 		}
1188 	}
1189 	if(which != NULL && err == TRUE) {
1190 		/* error, free up everything */
1191 #ifdef HAVE_REGEX_H
1192 		if(which->ptrs != NULL) {
1193 			free(which->ptrs);
1194 		}
1195 #endif
1196 		if(which->string != NULL) {
1197 			free(which->string);
1198 		}
1199 		if(which->header != NULL) {
1200 			free(which->header);
1201 		}
1202 		if(which != NULL) {
1203 			free(which);
1204 			which = NULL;
1205 		}
1206 	}
1207 
1208 	return which;
1209 }
1210 /*------------------------------------------------------------------------------------*/
1211 int regex_check(char *headerbuf, pmy_regex expr, int debug) {
1212 	int match = FALSE;
1213 	char *startline, *endline;
1214 
1215 	if(expr->header != NULL) {
1216 		if((startline = strstr(headerbuf, expr->header)) != NULL) {
1217 			endline = strchr(startline, '\n');
1218 			/* end this line, so we only search the right header line */
1219 			if(endline != NULL) {
1220 				*endline = '\0';
1221 			}
1222 			/* the +strlen(expr->header) so the header field and the space */
1223 			/* immediately following it aren't included in the search */
1224 			if(debug == TRUE) {
1225 				do_debug("checking -%s- for -%s-\n", startline+strlen(expr->header)+1,
1226 					 expr->string);
1227 			}
1228 #ifdef HAVE_REGEX_H
1229 			if(expr->ptrs != NULL) {
1230 
1231 
1232 				if(regexec(expr->ptrs, startline+strlen(expr->header)+1, 0, NULL, 0) == 0) {
1233 					match = TRUE;
1234 				}
1235 			}
1236 			else {
1237 #endif
1238 				match = find_string(debug, expr, startline+strlen(expr->header)+1);
1239 #ifdef HAVE_REGEX_H
1240 			}
1241 #endif
1242 			/* put the nl back on that we deleted */
1243 			if(endline != NULL) {
1244 				*endline = '\n';
1245 			}
1246 		}
1247 	}
1248 	if(debug== TRUE) {
1249 		do_debug("Returning match=%s\n", (match == TRUE) ? "True" : "False");
1250 	}
1251 
1252 	return match;
1253 }
1254 /*------------------------------------------------------------------------------------*/
1255 int regex_block(char *what, pmy_regex where, int debug) {
1256 
1257 	/* find a string in a block */
1258 	int match = FALSE;
1259 
1260 	if(debug == TRUE && where->string != NULL) {
1261 		do_debug("Checking for -%s-\n", where->string);
1262 	}
1263 #ifdef HAVE_REGEX_H
1264 	if(where->ptrs != NULL) {
1265 		if(regexec(where->ptrs, what, 0, NULL, 0) == 0) {
1266 			match = TRUE;
1267 		}
1268 	}
1269 	else {
1270 #endif
1271 		match = find_string(debug, where, what);
1272 #ifdef HAVE_REGEX_H
1273 	}
1274 #endif
1275 	return match;
1276 }
1277 /*---------------------------------------------------------------------------------------------*/
1278 int find_string(int debug, pmy_regex expr, char *str) {
1279 
1280 	/* the search mechanism is based on the Boyer-Moor Algorithms (pg 286- of Algorithms book) */
1281 	int match = FALSE;
1282 	char *ptr;		/* pointer to pattern */
1283 	int lenp;		/* length of pattern */
1284 	int lens;		/* length of string we are searching  */
1285 	int i;			/* current location in string */
1286 	int j;			/* current location in pattern */
1287 	int t;			/* value of skip array for string location */
1288 
1289 	ptr = expr->string;
1290 	lenp = strlen(ptr);
1291 	lens = strlen(str);
1292 
1293 
1294 	if(expr->case_sensitive == TRUE ) {
1295 		/* do case-sensitive search */
1296 		if(debug == TRUE) {
1297 			do_debug("Case-sensitive search for %s\n", ptr);
1298 		}
1299 		i = j = lenp-1;
1300 		do {
1301 			if(str[i] == ptr[j]) {
1302 				i--;
1303 				j--;
1304 			}
1305 			else {
1306 				t = expr->skiparray[(unsigned char) str[i]];
1307 				i += ( lenp -j > t) ? (lenp - j) : t;
1308 				j = lenp - 1;
1309 			}
1310 		} while( j >= 0 && i < lens);
1311 		if(j < 0) {
1312 			match = TRUE;
1313 		}
1314 
1315 	}
1316 	else {
1317 		/* do case-insensitive search */
1318 		if(debug == TRUE ) {
1319 			do_debug("Case-insensitive search for --%s--\n", ptr);
1320 		}
1321 		i = j = lenp-1;
1322 		do {
1323 			if(tolower(str[i]) == tolower(ptr[j])) {
1324 				i--;
1325 				j--;
1326 			}
1327 			else {
1328 				t = expr->skiparray[(unsigned char) str[i]];
1329 				i += ( lenp -j > t) ? (lenp - j) : t;
1330 				j = lenp - 1;
1331 			}
1332 		} while( j >= 0 && i < lens);
1333 		if(j < 0) {
1334 			match = TRUE;
1335 		}
1336 	}
1337 	return match;
1338 
1339 }
1340 /*------------------------------------------------------------------------------------*/
1341 void print_debug(PKillStruct master, const char *fname) {
1342 	int i;
1343 
1344 	/* print out status of master killstruct to debug file */
1345 	do_debug("--%s-- Master KillStruct\n",fname);
1346 	do_debug("logyn=%s\n",true_str(master->logyn));
1347 	do_debug("grp_override=%s\n",true_str(master->grp_override));
1348  	do_debug("tie_delete=%s\n",true_str(master->tie_delete));
1349 	do_debug("totgrps=%d\n", master->totgrps);
1350 	do_debug("ignore_postfix=%s\n",true_str(master->ignore_postfix));
1351 	do_debug("use_extended_regex=%s\n", true_str(master->use_extended_regex));
1352 	do_debug("xover_long_long=%s\n", true_str(master->xover_log_long));
1353 
1354 	do_debug("Master kill group");
1355 	debug_one_kill(&(master->master));
1356 
1357 	for(i = 0 ; i < master->totgrps ; i++ ) {
1358 		do_debug("Group %d = %s\n", i, master->grps[i].group);
1359 		do_debug("--delkeep =%s\n", (master->grps[i].delkeep == DELKEEP_KEEP) ? "keep" : "delete");
1360 		debug_one_kill(&(master->grps[i].match));
1361 
1362 	}
1363 	do_debug("--%s-- End of Killstruct\n", fname);
1364 }
1365 /*------------------------------------------------------------------------*/
1366 void debug_one_kill(POneKill ptr) {
1367 	pmy_regex temp;
1368 
1369 	do_debug("--hilines =%d\n", ptr->hilines);
1370 	do_debug("--lowlines=%d\n", ptr->lowlines);
1371 	do_debug("--maxgrps=%d\n", ptr->maxgrps);
1372 	do_debug("--maxxref=%d\n", ptr->maxxref);
1373 	do_debug("--bodysize>%lu\n", ptr->bodybig);
1374 	do_debug("--bodysize<%lu\n", ptr->bodysmall);
1375 	do_debug("--quote=%c\n", ptr->quote);
1376 	do_debug("--non_regex=%c\n", ptr->non_regex);
1377 #ifdef HAVE_REGEX_H
1378 	do_debug("--use_extended_regex=%s\n", true_str(ptr->use_extended));
1379 #endif
1380 	temp= ptr->header;
1381 	while(temp != NULL) {
1382 #ifdef HAVE_REGEX_H
1383 		do_debug("--header scan is %sregex\n", (temp->ptrs == NULL) ? "non-" : "");
1384 #endif
1385 		do_debug("--Scan is %scase-sensitive\n", (temp->case_sensitive == TRUE) ? "" : "non-");
1386 		if(temp->string != NULL) {
1387 			do_debug("--header scan=%s\n", temp->string);
1388 		}
1389 		temp = temp->next;
1390 	}
1391 	temp = ptr->body;
1392 	while(temp != NULL) {
1393 #ifdef HAVE_REGEX_H
1394 		do_debug("--body scan is %sregex\n", (temp->ptrs == NULL) ? "non-" : "");
1395 #endif
1396 		do_debug("--Scan is %scase-sensitive\n", (temp->case_sensitive == TRUE) ? "" : "non-");
1397 		if(temp->string != NULL) {
1398 			do_debug("--body  scan=%s\n", temp->string);
1399 		}
1400 		temp = temp->next;
1401 	}
1402 	temp = ptr->list;
1403 	while(temp != NULL) {
1404 #ifdef HAVE_REGEX_H
1405 		do_debug("--header match is %sregex\n", (temp->ptrs == NULL) ? "non-" : "");
1406 #endif
1407 		do_debug("--Scan is %scase-sensitive\n", (temp->case_sensitive == TRUE) ? "" : "non-");
1408 		do_debug("--header match= %s %s\n", temp->header, temp->string);
1409 		temp = temp->next;
1410 	}
1411 	do_debug("--end of Killfile Group--\n");
1412 }
1413 
1414