/* This file Copyright 1992 by Clifford A. Adams */
/* score.c
*
*/
#include "EXTERN.h"
#include "common.h"
#ifdef SCORE
/* if SCORE is undefined, no code should be compiled */
/* sort the following includes later */
#include "list.h"
#include "hash.h"
#include "cache.h"
#include "bits.h"
#include "artio.h" /* for openart var.*/
#include "final.h" /* int_count */
#include "head.h" /* ? */
#include "intrp.h" /* for filexp */
#include "ng.h" /* art */
#include "ngdata.h"
#include "search.h" /* for regex */
#include "rt-util.h" /* spinner */
#include "term.h" /* input_pending() */
#include "trn.h" /* ngname */
#include "util.h" /* several */
#ifdef SCAN
#include "scan.h"
#include "sorder.h"
#include "scanart.h"
#ifdef SCAN_ART
#include "samain.h"
#include "samisc.h"
#endif
#endif
#include "INTERN.h"
#include "score.h"
#include "EXTERN.h"
#include "scorefile.h"
#include "scoresave.h"
#include "score-easy.h" /* interactive menus and such */
#ifdef USE_FILTER
#include "filter.h"
#endif
#include "INTERN.h" /* not currently needed, but safer */
#if defined(USE_FILTER) && defined(FILTER_DEBUG)
FILE* filter_error_file;
#endif
void
sc_init(pend_wait)
bool_int pend_wait; /* if true, enter pending mode when scoring... */
{
int i;
ART_NUM a;
if (lastart == 0 || lastart < absfirst) {
#if 0
printf("No articles exist to be scored.\n") FLUSH;
#endif
return;
}
sc_sf_force_init = TRUE; /* generally force initialization */
if (sc_delay) /* requested delay? */
return;
sc_sf_delay = FALSE;
/* Consider the relationships between scoring and article scan mode.
* Should one be able to initialize the other? How much can they depend on
* each other when both are #defined?
* Consider this especially in a later redesign or porting these systems
* to other newsreaders.
*/
kill_thresh_active = FALSE; /* kill thresholds are generic */
/* July 24, 1993: changed default of sc_savescores to TRUE */
sc_savescores = TRUE;
/* CONSIDER: (for sc_init callers) is lastart properly set yet? */
sc_fill_max = absfirst - 1;
#ifdef SCAN_ART
if (sa_mode_read_elig || firstart > lastart)
sc_fill_read = TRUE;
else
#endif
sc_fill_read = FALSE;
if (sf_verbose) {
printf("\nScoring articles...");
fflush(stdout); /* print it *now* */
}
sc_initialized = TRUE; /* little white lie for lookahead */
/* now is a good time to load a saved score-list which may exist */
if (!sc_rescoring) { /* don't load if rescoring */
sc_load_scores(); /* will be quiet if non-existent */
i = firstart;
if (sc_fill_read)
i = absfirst;
if (sc_sf_force_init)
i = lastart+1; /* skip loop */
for (i = article_first(i); i <= lastart; i = article_next(i)) {
if (!SCORED(i) && (sc_fill_read || article_unread(i)))
break;
}
if (i == lastart) /* all scored */
sc_sf_delay = TRUE;
}
if (sc_sf_force_init)
sc_sf_delay = FALSE;
if (!sc_sf_delay)
sf_init(); /* initialize the scorefile code */
sc_do_spin = FALSE;
for (i = article_last(lastart); i >= absfirst; i = article_prev(i)) {
if (SCORED(i))
break;
}
if (i < absfirst) { /* none scored yet */
/* score one article, or give up */
for (a = article_last(lastart); a >= absfirst; a = article_prev(a)) {
sc_score_art(a,TRUE); /* I want it *now* */
if (SCORED(a))
break;
}
if (a < absfirst) { /* no articles scored */
if (sf_verbose)
printf("\nNo articles available for scoring\n");
sc_cleanup();
return;
}
}
/* if no scoring rules/methods are present, score everything */
/* XXX will be bad if later methods are added. */
if (!sf_num_entries) {
/* score everything really fast */
for (a = article_last(lastart); a >= absfirst; a = article_prev(a))
sc_score_art(a,TRUE);
}
if (pend_wait) {
bool waitflag; /* if true, use key pause */
waitflag = TRUE; /* normal mode: wait for key first */
if (sf_verbose && waitflag) {
#ifdef PENDING
printf("(press key to start reading)");
#else
printf("(interrupt to start reading)");
#endif
fflush(stdout);
}
if (waitflag) {
setspin(SPIN_FOREGROUND);
sc_do_spin = TRUE; /* really do it */
}
sc_lookahead(TRUE,waitflag); /* jump in *now* */
if (waitflag) {
sc_do_spin = FALSE;
setspin(SPIN_POP);
}
}
if (sf_verbose)
putchar('\n') FLUSH;
sc_initialized = TRUE;
}
void
sc_cleanup()
{
if (!sc_initialized)
return;
if (sc_savescores)
sc_save_scores();
sc_loaded_count = 0;
if (sf_verbose) {
printf("\nCleaning up scoring...");
fflush(stdout);
}
if (!sc_sf_delay)
sf_clean(); /* let the scorefile do whatever cleaning it needs */
sc_initialized = FALSE;
if (sf_verbose)
printf("Done.\n") FLUSH;
}
void
sc_set_score(a,score)
ART_NUM a;
int score;
{
register ARTICLE* ap;
if (is_unavailable(a)) /* newly unavailable */
return;
if (kill_thresh_active && score <= kill_thresh && article_unread(a))
oneless_artnum(a);
ap = article_ptr(a);
ap->score = score; /* update the score */
ap->scoreflags |= SFLAG_SCORED;
#ifdef SCAN
s_order_changed = TRUE; /* resort */
#endif
}
/* Hopefully people will write more scoring routines later */
/* This is where you should add hooks for new scoring methods. */
void
sc_score_art_basic(a)
ART_NUM a;
{
int score;
#ifdef USE_FILTER
int sc;
# ifdef FILTER_DEBUG
ARTICLE* ap;
# endif
#endif
score = 0;
score += sf_score(a); /* get a score */
#ifdef USE_FILTER
# ifdef FILTER_DEBUG
filter_error_file = fopen("/tmp/score.log", "a");
# endif
sc = filter(a);
score += sc;
# ifdef FILTER_DEBUG
ap = article_find(a);
if (ap && ap->refs)
fprintf(filter_error_file, "%s: article %ld got score %ld\n",
current_ng->rcline, (long)a, (long)sc);
fclose(filter_error_file);
# endif /* FILTER_DEBUG */
#endif /* USE_FILTER */
if (sc_do_spin) /* appropriate to spin */
spin(20); /* keep the user amused */
sc_set_score(a,score); /* set the score */
}
/* Returns an article's score, scoring it if necessary */
int
sc_score_art(a,now)
ART_NUM a;
bool_int now; /* if TRUE, sort the scores if necessary... */
{
if (a < absfirst || a > lastart) {
#if 0
printf("\nsc_score_art: illegal article# %d\n",a) FLUSH;
#endif
return LOWSCORE; /* definitely unavailable */
}
if (is_unavailable(a))
return LOWSCORE;
if (sc_initialized == FALSE) {
sc_delay = FALSE;
sc_sf_force_init = TRUE;
sc_init(FALSE);
sc_sf_force_init = FALSE;
}
if (!SCORED(a)) {
if (sc_sf_delay) {
sf_init();
sc_sf_delay = FALSE;
}
sc_score_art_basic(a);
}
if (is_unavailable(a))
return LOWSCORE;
return article_ptr(a)->score;
}
/* scores articles in a range */
/* CONSIDER: option for scoring only unread articles (obey sc_fill_unread?) */
void
sc_fill_scorelist(first,last)
ART_NUM first,last;
{
int i;
for (i = article_first(first); i <= last; i = article_next(i))
(void)sc_score_art(i,FALSE); /* will be sorted later... */
}
/* consider having this return a flag (is finished/is not finished) */
/* flag == TRUE means sort now, FALSE means wait until later (not used)
* nowait == TRUE means start scoring immediately (group entry)
* FALSE means use NICEBG if available
*/
void
sc_lookahead(flag, nowait)
bool_int flag;
bool_int nowait;
{
ART_NUM oldart = openart;
ART_POS oldartpos;
if (!sc_initialized)
return; /* no looking ahead now */
#ifdef PENDING
if (input_pending())
return; /* delay as little as possible */
#endif
if (!sc_initialized)
return; /* don't score then... */
#ifdef PENDING
#ifdef NICEBG
if (sc_mode_nicebg && !nowait)
if (wait_key_pause(10)) /* wait up to 1 second for a key */
return;
#endif
#endif
if (oldart) /* Was there an article open? */
oldartpos = tellart(); /* where were we in it? */
#ifndef PENDING
if (int_count)
int_count = 0; /* clear the interrupt count */
#endif
/* prevent needless looping below */
if (sc_fill_max < firstart && !sc_fill_read)
sc_fill_max = article_first(firstart)-1;
else
sc_fill_max = article_first(sc_fill_max);
while (sc_fill_max < lastart
#ifdef PENDING
&& !input_pending()
#endif
) {
#ifndef PENDING
if (int_count > 0) {
int_count = 0;
return; /* user requested break */
}
#endif
sc_fill_max = article_next(sc_fill_max);
/* skip over some articles quickly */
while (sc_fill_max < lastart
&& (SCORED(sc_fill_max)
|| (!sc_fill_read && !article_unread(sc_fill_max))))
sc_fill_max = article_next(sc_fill_max);
if (SCORED(sc_fill_max))
continue;
if (!sc_fill_read) /* score only unread */
if (!article_unread(sc_fill_max))
continue;
(void)sc_score_art(sc_fill_max,FALSE);
}
if (oldart) /* copied from cheat.c */
artopen(oldart,oldartpos); /* do not screw the pager */
}
int
sc_percent_scored()
{
int i,total,scored;
if (!sc_initialized)
return 0; /* none scored */
if (sc_fill_max == lastart)
return 100;
i = firstart;
#ifdef SCAN_ART
if (sa_mode_read_elig)
i = absfirst;
#endif
total = scored = 0;
for (i = article_first(i); i <= lastart; i = article_next(i)) {
if (!article_exists(i))
continue;
if (!article_unread(i)
#ifdef SCAN_ART
&& !sa_mode_read_elig
#endif
)
continue;
total++;
if (SCORED(i))
scored++;
} /* for */
if (total == 0)
return 0;
return (scored*100) / total;
}
void
sc_rescore_arts()
{
ART_NUM a;
bool old_spin;
if (!sc_initialized) {
if (sc_delay) {
sc_delay = FALSE;
sc_sf_force_init = TRUE;
sc_init(TRUE);
sc_sf_force_init = FALSE;
}
} else if (sc_sf_delay) {
sf_init();
sc_sf_delay = FALSE;
}
if (!sc_initialized) {
printf("\nScoring is not initialized, aborting command.\n") FLUSH;
return;
}
/* I think sc_do_spin will always be false, but why take chances? */
old_spin = sc_do_spin;
setspin(SPIN_FOREGROUND);
sc_do_spin = TRUE; /* amuse the user */
for (a = article_first(absfirst); a <= lastart; a = article_next(a)) {
if (article_exists(a))
sc_score_art_basic(a); /* rescore it then */
}
sc_do_spin = old_spin;
setspin(SPIN_POP);
#ifdef SCAN_ART
if (sa_in) {
s_ref_all = TRUE;
s_refill = TRUE;
s_top_ent = 0; /* make sure the refill starts from top */
}
#endif
}
/* Wrapper to isolate scorefile functions from the rest of the world */
/* corrupted (:-) 11/12/92 by CAA for online rescoring */
void
sc_append(line)
char* line;
{
char filechar;
if (!line) /* empty line */
return;
if (!sc_initialized) {
if (sc_delay) {
sc_delay = FALSE;
sc_sf_force_init = TRUE;
sc_init(TRUE);
sc_sf_force_init = FALSE;
}
} else if (sc_sf_delay) {
sf_init();
sc_sf_delay = FALSE;
}
if (!sc_initialized) {
printf("\nScoring is not initialized, aborting command.\n") FLUSH;
return;
}
if (!*line) {
line = sc_easy_append();
if (!line)
return; /* do nothing with empty string */
}
filechar = *line; /* first char */
sf_append(line);
if (filechar == '!') {
printf("\nRescoring articles...");
fflush(stdout);
sc_rescore_arts();
printf("Done.\n") FLUSH;
#ifdef SCAN_ART
if (sa_initialized)
s_top_ent = -1; /* reset top of page */
#endif
}
}
void
sc_rescore()
{
sc_rescoring = TRUE; /* in case routines need to know */
sc_cleanup(); /* get rid of the old */
sc_init(TRUE); /* enter the new... (wait for rescore) */
#ifdef SCAN_ART
if (sa_initialized) {
s_top_ent = -1; /* reset top of page */
s_refill = TRUE; /* make sure a refill is done */
}
#endif
sc_rescoring = FALSE;
}
/* May have a very different interface in the user versions */
void
sc_score_cmd(line)
char* line;
{
long i, j;
char* s;
if (!sc_initialized) {
if (sc_delay) {
sc_delay = FALSE;
sc_sf_force_init = TRUE;
sc_init(TRUE);
sc_sf_force_init = FALSE;
}
} else if (sc_sf_delay) {
sf_init();
sc_sf_delay = FALSE;
}
if (!sc_initialized) {
printf("\nScoring is not initialized, aborting command.\n") FLUSH;
return;
}
if (!*line) {
line = sc_easy_command();
if (!line)
return; /* do nothing with empty string */
if (*line == '\"') {
buf[0] = '\0';
sc_append(buf);
return;
}
}
switch (*line) {
case 'f': /* fill (useful when PENDING is unavailable) */
printf("Scoring more articles...");
fflush(stdout); /* print it now */
setspin(SPIN_FOREGROUND);
sc_do_spin = TRUE;
sc_lookahead(TRUE,FALSE);
sc_do_spin = FALSE;
setspin(SPIN_POP);
/* consider a "done" message later,
* *if* lookahead did all the arts */
putchar('\n') FLUSH;
break;
case 'r': /* rescore */
printf("Rescoring articles...\n") FLUSH;
sc_rescore();
break;
case 's': /* verbose score for this article */
/* XXX CONSIDER: A VERBOSE-SCORE ROUTINE (instead of this hack) */
i = 0; /* total score */
sf_score_verbose = TRUE;
j = sf_score(art);
sf_score_verbose = FALSE;
printf("Scorefile total score: %ld\n",j);
i += j;
j = sc_score_art(art,TRUE);
if (i != j) {
/* Consider resubmitting article to filter? */
printf("Other scoring total: %ld\n", j - i) FLUSH;
}
printf("Total score is %ld\n",i) FLUSH;
break;
case 'e': /* edit scorefile or other file */
for (s = line+1; *s == ' ' || *s == '\t'; s++) ;
if (!*s) /* empty name for scorefile */
sf_edit_file("\""); /* edit local scorefile */
else
sf_edit_file(s);
break;
default:
printf("Unknown scoring command |%s|\n",line);
} /* switch */
}
void
sc_kill_threshold(thresh)
int thresh; /* kill all articles with this score or lower */
{
ART_NUM a;
for (a = article_first(firstart); a <= lastart; a = article_next(a)) {
if (article_ptr(a)->score <= thresh && article_unread(a)
#ifdef SCAN_ART
/* CAA 6/19/93: this is needed for zoom mode */
&& sa_basic_elig(sa_artnum_to_ent(a))
#endif
)
oneless_artnum(a);
}
}
#endif /* SCORE */
syntax highlighted by Code2HTML, v. 0.9.1