1 /*
2 This file is part of SLRN.
3
4 Copyright (c) 1994, 1999 John E. Davis <davis@space.mit.edu>
5
6 This program is free software; you can redistribute it and/or modify it
7 under the terms of the GNU General Public License as published by the Free
8 Software Foundation; either version 2 of the License, or (at your option)
9 any later version.
10
11 This program is distributed in the hope that it will be useful, but WITHOUT
12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 more details.
15
16 You should have received a copy of the GNU General Public License along
17 with this program; if not, write to the Free Software Foundation, Inc., 675
18 Mass Ave, Cambridge, MA 02139, USA.
19 */
20 #include "config.h"
21 #ifndef SLRNPULL_CODE
22 # include "slrnfeat.h"
23 #endif
24
25 #include <stdio.h>
26 #include <string.h>
27 #include <sys/types.h>
28 #include <time.h>
29
30 #ifdef HAVE_STDLIB_H
31 # include <stdlib.h>
32 #endif
33
34 #include <slang.h>
35 #include "jdmacros.h"
36
37 #ifndef SLRNPULL_CODE
38 # include "slrn.h"
39 # include "group.h"
40 # include "misc.h"
41 # include "server.h"
42 # include "hash.h"
43 # include "util.h"
44 #endif
45
46 #ifdef KANJI
47 #include "chmap.h"
48 #endif
49 #include "art.h"
50 #include "xover.h"
51 #include "score.h"
52
53 /* The score file will implement article scoring for slrn. Basically the idea
54 * is that the user will provide a set of regular expressions that
55 * will act on the header of an article and return an integer or
56 * 'score'. If this score is less than zero, the article will be
57 * killed. If it is a positive number greater than some user defined
58 * value, the article will be flagged as interesting. If the score
59 * is zero, the article is not killed and it is not flagged as
60 * interesting either.
61 */
62
63 int Slrn_Perform_Scoring = SLRN_XOVER_SCORING | SLRN_EXPENSIVE_SCORING;
64
65 /* These two structures are pseudo-score types containing no compiled
66 * regular expressions.
67 */
68 typedef struct PScore_Regexp_Type
69 {
70 #define MAX_KEYWORD_LEN 24
71 char keyword[MAX_KEYWORD_LEN]; /* used only by generic type */
72 unsigned int header_type;
73 #define SCORE_SUBJECT 1
74 #define SCORE_FROM 2
75 #define SCORE_XREF 3
76 #define SCORE_NEWSGROUP 4
77 #define SCORE_REFERENCES 5
78 #define SCORE_LINES 6
79 #define SCORE_MESSAGE_ID 7
80 #define SCORE_DATE 8
81 /* generic requires extra server interaction */
82 #define SCORE_GENERIC 16
83 unsigned int flags;
84 #define NOT_FLAG 1
85 #define USE_INTEGER 2
86 union
87 {
88 unsigned char *regexp_str;
89 int ival;
90 }
91 ireg;
92 struct PScore_Regexp_Type *next;
93 }
94 PScore_Regexp_Type;
95
96 typedef struct PScore_Type
97 {
98 int score;
99 unsigned int flags;
100 #define RETURN_THIS_SCORE 1
101 #define SCORE_IS_OR_TYPE 2
102 struct PScore_Regexp_Type *pregexp_list;
103 struct PScore_Type *next;
104 }
105 PScore_Type;
106
107 typedef struct
108 {
109 SLRegexp_Type regexp;
110 unsigned char buf[512]; /* for compiled pattern */
111 }
112 Our_SLRegexp_Type;
113
114 /* These two structures are compiled versions of the above. */
115 typedef struct Score_Regexp_Type
116 {
117 unsigned int header_type;
118 char *generic_keyword; /* pointer to space in pscore */
119 int not_flag;
120 int do_osearch;
121 union
122 {
123 int ival; /* used by certain headers */
124 Our_SLRegexp_Type re;
125 SLsearch_Type se;
126 }
127 search;
128 struct Score_Regexp_Type *next;
129 }
130 Score_Regexp_Type;
131
132 typedef struct Score_Type
133 {
134 PScore_Type *pscore; /* points at structure this is derived from */
135 struct Score_Regexp_Type regexp_list;
136 struct Score_Type *next;
137 }
138 Score_Type;
139
140 #define MAX_GROUP_REGEXP_SIZE 256
141 #define MAX_GROUP_NAME_LEN 80
142
143 typedef struct Group_Score_Name_Type
144 {
145 SLRegexp_Type group_regexp;
146 char name[MAX_GROUP_NAME_LEN]; /* group name or pattern */
147 unsigned char buf[MAX_GROUP_REGEXP_SIZE];/* for compiled pattern */
148 struct Group_Score_Name_Type *next;
149 }
150 Group_Score_Name_Type;
151
152 typedef struct Group_Score_Type
153 {
154 unsigned int gst_not_flag;
155 Group_Score_Name_Type gsnt;
156 PScore_Type *pst;
157 struct Group_Score_Type *next;
158 }
159 Group_Score_Type;
160
161 static Group_Score_Type *Group_Score_Root;
162 static Group_Score_Type *Group_Score_Next;
163 static Score_Type *Score_Root, *Score_Tail;
164
165 int Slrn_Apply_Score = 1;
166
slrn_score_header(Slrn_Header_Type * h,char * newsgroup)167 int slrn_score_header (Slrn_Header_Type *h, char *newsgroup)
168 {
169 Score_Type *st;
170 int score = 0;
171 char *s;
172 int ival = 0;
173
174 #ifndef SLRNPULL_CODE
175 #if SLRN_HAS_MSGID_CACHE
176 s = slrn_is_msgid_cached (h->msgid, newsgroup, 1);
177 if (s != NULL)
178 {
179 /* Kill it if this wasn't the newsgroup where we saw it */
180 if (strcmp (s, newsgroup))
181 return Slrn_Kill_Score_Max;
182 }
183 #endif
184 #endif /* NOT SLRNPULL_CODE */
185
186 s = NULL;
187
188 st = Score_Root;
189 while (st != NULL)
190 {
191 unsigned int len;
192 Score_Regexp_Type *srt = &st->regexp_list;
193 #ifndef SLRNPULL_CODE
194 char buf[1024];
195 #endif
196 int or_type = st->pscore->flags & SCORE_IS_OR_TYPE;
197
198 while (srt != NULL)
199 {
200 switch (srt->header_type)
201 {
202 case SCORE_LINES:
203 ival = h->lines;
204 goto integer_compare;/* ugly, ugly, ugly!! */
205
206 case SCORE_SUBJECT:
207 s = h->subject;
208 break;
209
210 case SCORE_FROM:
211 s = h->from;
212 break;
213
214 case SCORE_DATE:
215 s = h->date;
216 break;
217
218 case SCORE_MESSAGE_ID:
219 s = h->msgid;
220 break;
221
222 case SCORE_XREF:
223 s = h->xref;
224 break;
225
226 case SCORE_REFERENCES:
227 s = h->refs;
228 break;
229
230 case SCORE_NEWSGROUP:
231 s = newsgroup;
232 break;
233
234 case SCORE_GENERIC:
235 #ifdef SLRNPULL_CODE
236 s = slrn_get_extra_xover_header (srt->generic_keyword);
237 #else
238
239 /* Yuk. This will be slow! */
240 if (Slrn_Score_After_XOver)
241 {
242 if (((Slrn_Perform_Scoring & SLRN_EXPENSIVE_SCORING) == 0)
243 || (-1 == Slrn_Server_Obj->sv_xhdr_command (srt->generic_keyword,
244 h->number, buf, sizeof(buf))))
245 s = NULL;
246 else s = buf;
247 }
248 else
249 {
250 if (Slrn_Perform_Scoring & SLRN_EXPENSIVE_SCORING)
251 s = slrn_get_extra_xover_header (srt->generic_keyword);
252 else s = NULL;
253 }
254 #endif
255 break;
256
257 default:
258 s = NULL; /* not supposed to happen */
259 }
260
261 if (s == NULL)
262 {
263 if (srt->not_flag)
264 {
265 /* Match */
266 if (or_type)
267 break;
268
269 goto next_srt;
270 }
271
272 /* Match failed */
273 if (or_type) goto next_srt;
274 break;
275 }
276
277 len = strlen (s);
278
279 if (srt->do_osearch)
280 {
281 SLsearch_Type *se = &srt->search.se;
282
283 if ((len < (unsigned int) se->key_len)
284 || (NULL == SLsearch ((unsigned char *) s,
285 (unsigned char *) s + len,
286 se)))
287 {
288 if (srt->not_flag == 0)
289 {
290 if (or_type) goto next_srt;
291 break;
292 }
293 }
294 else if (srt->not_flag)
295 {
296 if (or_type) goto next_srt;
297 break;
298 }
299 }
300 else
301 {
302 SLRegexp_Type *re;
303
304 re = &srt->search.re.regexp;
305 if ((len < re->min_length)
306 || (NULL == SLang_regexp_match ((unsigned char *)s, len, re)))
307 {
308 if (srt->not_flag == 0)
309 {
310 if (or_type) goto next_srt;
311 break;
312 }
313 }
314 else if (srt->not_flag)
315 {
316 if (or_type) goto next_srt;
317 break;
318 }
319 }
320 /* Get here if above matched */
321 if (or_type) break;
322 srt = srt->next;
323 continue;
324
325 /* This is ugly but I am worried about speed. --- we only get
326 * here for those headers that have integer values.
327 */
328 integer_compare:
329 if (ival < srt->search.ival)
330 {
331 if (srt->not_flag == 0)
332 {
333 if (or_type) goto next_srt;
334 break;
335 }
336 }
337 else if (srt->not_flag)
338 {
339 if (or_type) goto next_srt;
340 break;
341 }
342
343 /* If we get here, the regular expression matched. */
344 if (or_type) break;
345
346 next_srt:
347 srt = srt->next;
348 }
349
350 if (((srt == NULL) && (or_type == 0))
351 || (or_type && (srt != NULL)))
352 {
353 int st_score = st->pscore->score;
354
355 if (st->pscore->flags & RETURN_THIS_SCORE)
356 return st_score;
357
358 if ((st_score == 9999)
359 || (st_score == -9999))
360 return st_score;
361
362 if (st_score == 0)
363 return score;
364
365 score += st_score;
366 }
367
368 st = st->next;
369 }
370 return score;
371 }
372
373
chain_group_regexp(PScore_Type * pst,int * generic)374 static int chain_group_regexp (PScore_Type *pst, int *generic)
375 {
376 PScore_Regexp_Type *psrt;
377 Score_Regexp_Type *srt;
378 Score_Type *st;
379 SLRegexp_Type *re;
380
381 while (pst != NULL)
382 {
383 st = (Score_Type *) slrn_malloc (sizeof (Score_Type), 1, 0);
384 if (st == NULL)
385 return -1;
386
387 if (Score_Root == NULL)
388 {
389 Score_Root = st;
390 }
391 else Score_Tail->next = st;
392
393 Score_Tail = st;
394
395 st->pscore = pst;
396
397 psrt = pst->pregexp_list;
398 srt = &st->regexp_list;
399
400 while (psrt != NULL)
401 {
402 unsigned int flags = psrt->flags;
403
404 if (SCORE_GENERIC == (srt->header_type = psrt->header_type))
405 *generic += 1;
406
407 srt->generic_keyword = psrt->keyword;
408 srt->not_flag = (0 != (flags & NOT_FLAG));
409
410 if (flags & USE_INTEGER)
411 {
412 srt->search.ival = psrt->ireg.ival;
413 }
414 else
415 {
416 re = &srt->search.re.regexp;
417 re->pat = psrt->ireg.regexp_str;
418 re->buf = srt->search.re.buf;
419 re->buf_len = sizeof (srt->search.re.buf);
420 re->case_sensitive = 0;
421
422 if (0 != SLang_regexp_compile (re))
423 {
424 return -1;
425 }
426
427 /* If an ordinary search is ok, use it. */
428 if (re->osearch)
429 {
430 srt->do_osearch = 1;
431 SLsearch_init ((char *) psrt->ireg.regexp_str, 1, 0, &srt->search.se);
432 }
433 }
434
435 psrt = psrt->next;
436 if (psrt == NULL) break;
437
438 srt->next = (Score_Regexp_Type *) slrn_malloc (sizeof (Score_Regexp_Type),
439 1, 0);
440 if (NULL == (srt = srt->next))
441 return -1;
442 }
443 pst = pst->next;
444 }
445 return 0;
446 }
447
free_group_chain(void)448 static void free_group_chain (void)
449 {
450 Score_Regexp_Type *srt;
451
452 while (Score_Root != NULL)
453 {
454 Score_Type *next = Score_Root->next;
455 srt = &Score_Root->regexp_list;
456 srt = srt->next; /* first not malloced */
457 while (srt != NULL)
458 {
459 Score_Regexp_Type *srt_next = srt->next;
460 SLFREE (srt);
461 srt = srt_next;
462 }
463 SLFREE (Score_Root);
464 Score_Root = next;
465 }
466 Score_Tail = NULL;
467 }
468
slrn_open_score(char * group_name)469 int slrn_open_score (char *group_name)
470 {
471 Group_Score_Type *gsc = Group_Score_Root;
472 unsigned int n;
473 int generic;
474
475 free_group_chain ();
476
477 if ((Slrn_Perform_Scoring == 0) || (Group_Score_Root == NULL))
478 {
479 Slrn_Apply_Score = 0;
480 return 0;
481 }
482
483 n = strlen (group_name);
484 generic = 0;
485 while (gsc != NULL)
486 {
487 Group_Score_Name_Type *gsnt;
488 int match;
489
490 gsnt = &gsc->gsnt;
491 match = 0;
492 while ((gsnt != NULL) && (match == 0))
493 {
494 SLRegexp_Type *re = &gsnt->group_regexp;
495
496 match = ((re->min_length <= n)
497 && (NULL != SLang_regexp_match ((unsigned char *) group_name, n, re)));
498 gsnt = gsnt->next;
499 }
500
501 if (gsc->gst_not_flag)
502 match = !match;
503
504 if (match)
505 {
506 if (-1 == chain_group_regexp (gsc->pst, &generic))
507 {
508 free_group_chain ();
509 return -1;
510 }
511 }
512 gsc = gsc->next;
513 }
514
515 if (Score_Root == NULL) return 0;
516
517 Slrn_Apply_Score = 1;
518
519 if ((generic == 0)
520 || (0 == (Slrn_Perform_Scoring & SLRN_EXPENSIVE_SCORING)))
521 return 1;
522
523 #ifndef SLRNPULL_CODE
524 if ((generic >= 1) || !Slrn_Server_Obj->sv_has_xover || !Slrn_Server_Obj->sv_has_cmd ("XHDR"))
525 {
526 Slrn_Score_After_XOver = 0;
527 slrn_open_suspend_xover ();
528 }
529 #endif
530
531 return 1;
532 }
533
slrn_close_score(void)534 void slrn_close_score (void)
535 {
536 free_group_chain ();
537 #ifndef SLRNPULL_CODE
538 slrn_close_suspend_xover ();
539 #endif
540 }
541
542
543
add_group_regexp(PScore_Type * pst,unsigned char * str,unsigned char * keyword,unsigned int type,int not_flag)544 static int add_group_regexp (PScore_Type *pst,
545 unsigned char *str,
546 unsigned char *keyword, unsigned int type, int not_flag)
547 {
548 unsigned int len;
549 PScore_Regexp_Type *psrt;
550
551 *str++ = 0; /* null terminate keyword by zeroing
552 * out the colon. This is by agreement
553 * with the calling routine.
554 */
555
556 if (*str == ' ') str++; /* space following colon not meaningful */
557
558 len = (unsigned int) (slrn_trim_string ((char *) str) - (char *) str);
559 if (0 == len) return -1;
560
561 psrt = (PScore_Regexp_Type *) slrn_safe_malloc (sizeof (PScore_Regexp_Type));
562
563 if (type != SCORE_LINES)
564 psrt->ireg.regexp_str = (unsigned char *) slrn_safe_strmalloc ((char *)str);
565 else
566 {
567 psrt->ireg.ival = atoi((char *)str);
568 psrt->flags |= USE_INTEGER;
569 }
570
571 psrt->header_type = type;
572 strncpy (psrt->keyword, (char *) keyword, MAX_KEYWORD_LEN);
573 psrt->keyword[MAX_KEYWORD_LEN - 1] = 0;
574
575 if (not_flag) psrt->flags |= NOT_FLAG;
576 psrt->next = NULL;
577 if (pst->pregexp_list == NULL)
578 {
579 pst->pregexp_list = psrt;
580 }
581 else
582 {
583 PScore_Regexp_Type *last = pst->pregexp_list;
584 while (last->next != NULL) last = last->next;
585 last->next = psrt;
586 }
587 return 0;
588 }
589
590
compile_group_names(char * group,Group_Score_Type * gst)591 static int compile_group_names (char *group, Group_Score_Type *gst)
592 {
593 char *comma;
594 Group_Score_Name_Type *gsnt;
595 unsigned int num_processed = 0;
596
597 gsnt = &gst->gsnt;
598
599 comma = group;
600 while (comma != NULL)
601 {
602 SLRegexp_Type *re;
603
604 group = slrn_skip_whitespace (group);
605
606 comma = slrn_strchr ((char *) group, ',');
607 if (comma != NULL)
608 {
609 *comma++ = 0;
610
611 while (1)
612 {
613 comma = slrn_skip_whitespace (comma);
614 if (*comma == 0)
615 {
616 comma = NULL;
617 break;
618 }
619 if (*comma != ',')
620 break;
621 comma++;
622 }
623 }
624
625 (void) slrn_trim_string (group);
626 if (*group == 0) continue;
627
628 strncpy (gsnt->name, group, MAX_GROUP_NAME_LEN - 1);
629 /* Note: because of the memset, this string is null terminated. */
630
631 re = &gsnt->group_regexp;
632 re->pat = (unsigned char *) slrn_fix_regexp ((char *)group);
633 re->buf = gsnt->buf;
634 re->buf_len = MAX_GROUP_REGEXP_SIZE;
635 re->case_sensitive = 0;
636 if (0 != SLang_regexp_compile (re))
637 {
638 return -1;
639 }
640
641 if (comma != NULL)
642 {
643 Group_Score_Name_Type *gsnt1;
644
645 gsnt1 = (Group_Score_Name_Type *) slrn_safe_malloc (sizeof (Group_Score_Name_Type));
646
647 gsnt->next = gsnt1;
648 gsnt = gsnt1;
649 group = comma;
650 }
651 num_processed++;
652 }
653 if (num_processed) return 0;
654 return -1;
655 }
656
create_new_score(unsigned char * group,int new_group_flag,unsigned int gst_not_flag,int score,unsigned int pscore_flags)657 static PScore_Type *create_new_score (unsigned char *group, int new_group_flag, unsigned int gst_not_flag,
658 int score, unsigned int pscore_flags)
659 {
660 PScore_Type *pst;
661
662 if (new_group_flag)
663 {
664 Group_Score_Type *gst, *tail;
665
666 tail = Group_Score_Next = Group_Score_Root;
667 while (Group_Score_Next != NULL)
668 {
669 if ((Group_Score_Next->gsnt.next == NULL)
670 && (Group_Score_Next->gst_not_flag == gst_not_flag)
671 && !strcmp ((char *) group, Group_Score_Next->gsnt.name))
672 break;
673 tail = Group_Score_Next;
674 Group_Score_Next = Group_Score_Next->next;
675 }
676
677 if (Group_Score_Next == NULL)
678 {
679 gst = (Group_Score_Type *) slrn_safe_malloc (sizeof (Group_Score_Type));
680
681 if (-1 == compile_group_names ((char *)group, gst))
682 return NULL;
683
684 gst->gst_not_flag = gst_not_flag;
685
686 if (Group_Score_Root == NULL)
687 {
688 Group_Score_Root = gst;
689 }
690 else tail->next = gst;
691
692 Group_Score_Next = gst;
693 }
694 }
695
696 /* Now create the PseudoScore type and add it. */
697 pst = (PScore_Type *) slrn_safe_malloc (sizeof (PScore_Type));
698
699 pst->score = score;
700 pst->flags = pscore_flags;
701
702 if (Group_Score_Next->pst == NULL)
703 {
704 Group_Score_Next->pst = pst;
705 }
706 else
707 {
708 PScore_Type *last = Group_Score_Next->pst;
709 while (last->next != NULL) last = last->next;
710 last->next = pst;
711 }
712 return pst;
713 }
714
715
score_error(char * msg,char * line,unsigned int linenum,char * file)716 static void score_error (char *msg, char *line, unsigned int linenum, char *file)
717 {
718 slrn_error ("Error processing %s\nLine %u:\n%s\n%s\n",
719 file, linenum, line, msg);
720 }
721
722
723
free_group_scores(void)724 static void free_group_scores (void)
725 {
726 while (Group_Score_Root != NULL)
727 {
728 Group_Score_Type *gnext = Group_Score_Root->next;
729 PScore_Type *pst = Group_Score_Root->pst;
730 Group_Score_Name_Type *gsnt;
731
732 gsnt = Group_Score_Root->gsnt.next;
733 while (gsnt != NULL)
734 {
735 Group_Score_Name_Type *next = gsnt->next;
736 SLFREE (gsnt);
737 gsnt = next;
738 }
739
740 while (pst != NULL)
741 {
742 PScore_Type *pnext = pst->next;
743 PScore_Regexp_Type *r = pst->pregexp_list;
744
745 while (r != NULL)
746 {
747 PScore_Regexp_Type *rnext = r->next;
748
749 if ((r->flags & USE_INTEGER) == 0)
750 slrn_free ((char *) r->ireg.regexp_str);
751 SLFREE (r);
752 r = rnext;
753 }
754 SLFREE (pst);
755 pst = pnext;
756 }
757
758 SLFREE (Group_Score_Root);
759 Group_Score_Root = gnext;
760 }
761 }
762
has_score_expired(unsigned char * s,unsigned long today)763 static int has_score_expired (unsigned char *s, unsigned long today)
764 {
765 unsigned long mm, dd, yyyy;
766 unsigned long score_time;
767
768 s = (unsigned char *) slrn_skip_whitespace ((char *) s);
769 if (*s == 0) return 0;
770
771 if (((3 != sscanf ((char *) s, "%lu/%lu/%lu", &mm, &dd, &yyyy))
772 && (3 != sscanf ((char *) s, "%lu-%lu-%lu", &dd, &mm, &yyyy)))
773 || (dd > 31)
774 || (mm > 12)
775 || (yyyy < 1900))
776 return -1;
777
778 score_time = (yyyy - 1900) * 10000 + (mm - 1) * 100 + dd;
779 if (score_time > today) return 0;
780 return 1;
781 }
782
get_today(void)783 static unsigned long get_today (void)
784 {
785 unsigned long mm, yy, dd;
786 time_t tloc;
787 struct tm *tm_struct;
788
789 time (&tloc);
790 tm_struct = localtime (&tloc);
791 yy = tm_struct->tm_year;
792 mm = tm_struct->tm_mon;
793 dd = tm_struct->tm_mday;
794
795 return yy * 10000 + mm * 100 + dd;
796 }
797
798 typedef struct
799 {
800 unsigned char group[256];
801 unsigned long today;
802 int score;
803 int start_new_group;
804 int gnt_not_flag;
805 int start_new_score;
806 int score_has_expired;
807 unsigned int pscore_flags;
808 PScore_Type *pst;
809 SLPreprocess_Type pt;
810 }
811 Score_Context_Type;
812
813 static int read_score_file_internal (char *, Score_Context_Type *);
814
handle_include_line(char * file,char * line,Score_Context_Type * c)815 static int handle_include_line (char *file, char *line, Score_Context_Type *c)
816 {
817 char *dir, *f;
818 char buf [SLRN_MAX_PATH_LEN];
819 int status;
820
821 line = slrn_skip_whitespace (line);
822
823 if (*line == 0)
824 return -1;
825
826 (void) slrn_trim_string (line);
827
828 #if defined(IBMPC_SYSTEM)
829 slrn_os2_convert_path (line);
830 #endif
831
832 /* Make a filename which is relative to dir of current file.
833 * Note that slrn_dircat will properly handle absolute filenames.
834 */
835
836 f = slrn_basename (file);
837 if (NULL == (dir = slrn_strnmalloc (file, (unsigned int) (f - file), 1)))
838 return -1;
839
840 if (-1 == slrn_dircat (dir, line, buf))
841 {
842 slrn_free (dir);
843 return -1;
844 }
845
846 status = read_score_file_internal (buf, c);
847 slrn_free (dir);
848 return status;
849 }
850
read_score_file_internal(char * file,Score_Context_Type * c)851 static int read_score_file_internal (char *file, Score_Context_Type *c)
852 {
853 char line[1024];
854 FILE *fp;
855 unsigned int linenum = 0;
856
857 if (NULL == (fp = fopen (file, "r")))
858 {
859 slrn_error ("Unable to open score file %s", file);
860 return -1;
861 }
862
863 while (fgets (line, sizeof (line) - 1, fp))
864 {
865 unsigned char *lp;
866 int not_flag;
867
868 #ifdef KANJI
869 /* editor �����ɤ� JIS �� SJIS �ξ�硢EUC �ˤ��롣*/
870 if (Slrn_Editor_Code != EUC) {
871 slrn_conv_string(line, EUC);
872 }
873 #endif
874 linenum++;
875
876 if (0 == SLprep_line_ok (line, &c->pt))
877 continue;
878
879 lp = (unsigned char *) slrn_skip_whitespace (line);
880 if ((*lp == '#') || (*lp == '%') || (*lp <= ' ')) continue;
881
882 if ((0 == slrn_case_strncmp (lp, (unsigned char *) "include", 7))
883 && ((lp[7] == ' ') || (lp[7] == '\t')))
884 {
885 if (0 == handle_include_line (file, (char *)lp + 7, c))
886 continue;
887
888 score_error ("Error handling INCLUDE line", line, linenum, file);
889 goto error_return;
890 }
891
892 if (*lp == '[')
893 {
894 unsigned char *g, *gmax, ch;
895 g = c->group;
896 gmax = g + sizeof (c->group);
897
898 lp++;
899 lp = (unsigned char *) slrn_skip_whitespace ((char *)lp);
900
901 c->gnt_not_flag = 0;
902 if (*lp == '~')
903 {
904 c->gnt_not_flag = 1;
905 lp = (unsigned char *) slrn_skip_whitespace ((char *)lp + 1);
906 }
907
908 while (((ch = *lp++) != 0)
909 && (ch != ']') && (g < gmax))
910 *g++ = ch;
911
912 if ((ch != ']') || (g == gmax))
913 {
914 score_error ("Syntax Error.", line, linenum, file);
915 goto error_return;
916 }
917 *g = 0;
918 c->start_new_group = 1;
919 c->score_has_expired = 0;
920 c->start_new_score = 0;
921 continue;
922 }
923
924 if (!slrn_case_strncmp (lp, (unsigned char *)"Score:", 6))
925 {
926 unsigned char *lpp = lp + 6;
927 c->pscore_flags = 0;
928 if (*lpp == ':')
929 {
930 lpp++;
931 c->pscore_flags |= SCORE_IS_OR_TYPE;
932 }
933 lpp = (unsigned char *) slrn_skip_whitespace ((char *) lpp);
934 if (*lpp == '=')
935 {
936 c->pscore_flags |= RETURN_THIS_SCORE;
937 lpp++;
938 }
939 c->score = atoi ((char *)lpp);
940 c->start_new_score = 1;
941 continue;
942 }
943
944 if (c->start_new_score)
945 {
946 if (!slrn_case_strncmp (lp, (unsigned char *) "Expires:", 8))
947 {
948 int ret;
949 ret = has_score_expired (lp + 8, c->today);
950 if (ret == -1)
951 {
952 score_error ("Expecting 'Expires: MM/DD/YYYY' or 'Expires: DD-MM-YYYY'",
953 line, linenum, file);
954 goto error_return;
955 }
956 if (ret)
957 {
958 slrn_message ("%s has expired score on line %d",
959 file, linenum);
960 c->start_new_score = 0;
961 c->score_has_expired = 1;
962 }
963 else c->score_has_expired = 0;
964 continue;
965 }
966
967
968 if (NULL == (c->pst = create_new_score (c->group, c->start_new_group, c->gnt_not_flag,
969 c->score, c->pscore_flags)))
970 {
971 score_error ("Bad group regular expression.", line, linenum, file);
972 goto error_return;
973 }
974 c->start_new_group = 0;
975 c->start_new_score = 0;
976 c->score_has_expired = 0;
977 }
978
979 if (c->score_has_expired) continue;
980
981 if (c->pst == NULL)
982 {
983 score_error ("Expecting Score keyword.", line, linenum, file);
984 goto error_return;
985 }
986
987 if (*lp == '~')
988 {
989 not_flag = 1;
990 lp++;
991 }
992 else not_flag = 0;
993
994 /* Otherwise the line is a kill one */
995 if (!slrn_case_strncmp (lp, (unsigned char *)"Subject:", 8))
996 add_group_regexp (c->pst, lp + 7, lp, SCORE_SUBJECT, not_flag);
997 else if (!slrn_case_strncmp (lp, (unsigned char *)"From:", 5))
998 add_group_regexp (c->pst, lp + 4, lp, SCORE_FROM, not_flag);
999 else if (!slrn_case_strncmp (lp, (unsigned char *)"Xref:", 5))
1000 add_group_regexp (c->pst, lp + 4, lp, SCORE_XREF, not_flag);
1001 else if (!slrn_case_strncmp (lp, (unsigned char *)"Newsgroup:", 10))
1002 add_group_regexp (c->pst, lp + 9, lp, SCORE_NEWSGROUP, not_flag);
1003 else if (!slrn_case_strncmp (lp, (unsigned char *)"References:", 11))
1004 add_group_regexp (c->pst, lp + 10, lp, SCORE_REFERENCES, not_flag);
1005 else if (!slrn_case_strncmp (lp, (unsigned char *)"Lines:", 6))
1006 add_group_regexp (c->pst, lp + 5, lp, SCORE_LINES, not_flag);
1007 else if (!slrn_case_strncmp (lp, (unsigned char *)"Date:", 5))
1008 add_group_regexp (c->pst, lp + 5, lp, SCORE_DATE, not_flag);
1009 else if (!slrn_case_strncmp (lp, (unsigned char *)"Message-Id:", 11))
1010 add_group_regexp (c->pst, lp + 10, lp, SCORE_MESSAGE_ID, not_flag);
1011 else
1012 {
1013 unsigned char *lpp = lp;
1014 while (*lpp && (*lpp != ':')) lpp++;
1015 if (*lpp != ':')
1016 {
1017 score_error ("Missing COLON.", line, linenum, file);
1018 goto error_return;
1019 }
1020
1021 add_group_regexp (c->pst, lpp, lp, SCORE_GENERIC, not_flag);
1022 }
1023 }
1024
1025 fclose (fp);
1026 return 0;
1027
1028 error_return:
1029 fclose (fp);
1030 return -1;
1031 }
1032
1033
slrn_read_score_file(char * file)1034 int slrn_read_score_file (char *file)
1035 {
1036 Score_Context_Type sc;
1037 int status;
1038
1039 if (Group_Score_Root != NULL)
1040 {
1041 free_group_scores ();
1042 }
1043
1044 sc.today = get_today ();
1045 sc.score = 0;
1046 sc.start_new_group = 1;
1047 sc.gnt_not_flag = 0;
1048 sc.start_new_score = 0;
1049 sc.score_has_expired = 0;
1050 sc.pscore_flags = 0;
1051 sc.pst = NULL;
1052 sc.group[0] = '*';
1053 sc.group[1] = 0;
1054
1055 (void) SLprep_open_prep (&sc.pt);
1056 status = read_score_file_internal (file, &sc);
1057 SLprep_close_prep (&sc.pt);
1058
1059 if (status == -1)
1060 Slrn_Apply_Score = 0;
1061 else
1062 Slrn_Apply_Score = 1;
1063
1064 return status;
1065 }
1066
1067