/* This file Copyright 1992 by Clifford A. Adams */
/* sacmd.c
*
* main command loop
*/
#include "EXTERN.h"
#include "common.h"
#ifdef SCAN_ART
#include "list.h"
#include "hash.h"
#include "cache.h"
/* absfirst, lastart, oneless_artnum() */
#include "bits.h"
#include "decode.h"
#include "term.h" /* for LINES */
#include "head.h"
#include "help.h" /* online help */
#include "ngdata.h" /* for ThreadedGroup */
#include "ng.h"
#include "nntpclient.h"
#include "datasrc.h"
#include "addng.h"
#include "ngstuff.h"
#include "respond.h"
#include "intrp.h"
#include "scan.h"
#include "scmd.h"
#include "smisc.h" /* needed? */
#include "sorder.h"
#include "spage.h"
#include "sdisp.h"
#include "scanart.h"
#include "samain.h"
#include "samisc.h"
#include "sadisp.h"
#include "sadesc.h"
#include "sathread.h"
#ifdef SCORE
#include "score.h"
#endif
#include "util.h"
#include "util2.h"
#include "INTERN.h"
#include "sacmd.h"
bool sa_extract_start();
/* use this command on an extracted file */
/*$$static char* sa_extracted_use = NULL;*/
static char* sa_extract_dest = NULL;
/* junk articles after extracting them */
static bool sa_extract_junk = FALSE;
/* several basic commands are already done by s_docmd (Scan level) */
/* interprets command in buf, returning 0 to continue looping,
* a condition code (negative #s) or an art# to read. Also responsible
* for setting refresh flags if necessary.
*/
int
sa_docmd()
{
long a; /* article pointed to */
long b; /* misc. artnum */
int i; /* for misc. purposes */
/*$$bool flag;*/ /* misc */
ART_NUM artnum;
a = (long)page_ents[s_ptr_page_line].entnum;
artnum = sa_ents[a].artnum;
switch(*buf) {
case '+': /* enter thread selector */
if (!ThreadedGroup) {
s_beep();
return 0;
}
buf[0] = '+'; /* fake up command for return */
buf[1] = '\0';
sa_art = artnum; /* give it somewhere to point */
s_save_context(); /* for possible later changes */
return SA_FAKE; /* fake up the command. */
#ifdef SCORE
case 'K': /* kill below a threshold */
*buf = ' '; /* for finish_cmd() */
if (!s_finish_cmd("Kill below or equal score:"))
break;
/* make **sure** that there is a number here */
i = atoi(buf+1);
if (i == 0) { /* it might not be a number */
char* s;
s = buf+1;
if (*s != '0' && ((*s != '+' && *s != '-') || s[1] != '0')) {
/* text was not a numeric 0 */
s_beep();
return 0; /* beep but do nothing */
}
}
sc_kill_threshold(i);
s_refill = TRUE;
s_ref_top = TRUE; /* refresh # of articles */
break;
#endif /* SCORE */
case 'D': /* kill unmarked "on" page */
for (i = 0; i <= s_bot_ent; i++)
/* This is a difficult decision, with no obviously good behavior. */
/* Do not kill threads with the first article marked, as it is probably
* not what the user wanted.
*/
if (!sa_marked(page_ents[i].entnum) || !sa_mode_fold)
(void)sa_art_cmd(sa_mode_fold,SA_KILL_UNMARKED,
page_ents[i].entnum);
/* consider: should it start reading? */
b = sa_readmarked_elig();
if (b) {
sa_clearmark(b);
return b;
}
s_ref_top = TRUE; /* refresh # of articles */
s_refill = TRUE;
break;
case 'J': /* kill marked "on" page */
/* If in "fold" mode, kill threads with the first article marked */
if (sa_mode_fold) {
for (i = 0; i <= s_bot_ent; i++) {
if (sa_marked(page_ents[i].entnum))
(void)sa_art_cmd(TRUE,SA_KILL,page_ents[i].entnum);
}
} else
for (i = 0; i <= s_bot_ent; i++)
(void)sa_art_cmd(FALSE,SA_KILL_MARKED,page_ents[i].entnum);
s_refill = TRUE;
s_ref_top = TRUE; /* refresh # of articles */
break;
case 'X': /* kill unmarked (basic-eligible) in group */
*buf = '?'; /* for finish_cmd() */
if (!s_finish_cmd("Junk all unmarked articles"))
break;
if ((buf[1] != 'Y') && (buf[1] != 'y'))
break;
i = s_first();
if (!i)
break;
if (!sa_basic_elig(i))
while ((i = s_next(i)) != 0 && !sa_basic_elig(i)) ;
/* New action: if in fold mode, will not delete an article which
has a thread-prior marked article. */
for ( ; i; i = s_next(i)) {
if (!sa_basic_elig(i))
continue;
if (!sa_marked(i) && !was_read(sa_ents[i].artnum)) {
if (sa_mode_fold) { /* new semantics */
long j;
/* make j == 0 if no prior marked articles in the thread. */
for (j = i; j; j = sa_subj_thread_prev(j))
if (sa_marked(j))
break;
if (j) /* there was a marked article */
continue; /* article selection loop */
}
oneless_artnum(sa_ents[i].artnum);
}
}
b = sa_readmarked_elig();
if (b) {
sa_clearmark(b);
return b;
}
s_refill = TRUE;
s_ref_top = TRUE; /* refresh # of articles */
break;
case 'c': /* catchup */
s_go_bot();
ask_catchup();
s_refill = TRUE;
s_ref_all = TRUE;
break;
#ifdef SCORE
case 'o': /* toggle between score and arrival orders */
s_rub_ptr();
if (sa_mode_order==1)
sa_mode_order = 2;
else
sa_mode_order = 1;
if (sa_mode_order == 2 && sc_delay) {
sc_delay = FALSE;
sc_init(TRUE);
}
if (sa_mode_order == 2 && !sc_initialized) /* score order */
sa_mode_order = 1; /* nope... (maybe allow later) */
/* if we go into score mode, make sure score is displayed */
if (sa_mode_order == 2 && !sa_mode_desc_score)
sa_mode_desc_score = TRUE;
s_sort();
s_go_top_ents();
s_refill = TRUE;
s_ref_bot = TRUE;
break;
case 'O': /* change article sorting order */
if (sa_mode_order != 2) { /* not in score order */
s_beep();
break;
}
score_newfirst = !score_newfirst;
s_sort();
s_go_top_ents();
s_refill = TRUE;
break;
case 'R': /* rescore articles */
if (!sc_initialized)
break;
/* clear to end of screen */
clear_rest();
s_ref_all = TRUE; /* refresh everything */
printf("\nRescoring articles...\n") FLUSH;
sc_rescore();
s_sort();
s_go_top_ents();
s_refill = TRUE;
eat_typeahead(); /* stay in control. */
break;
case Ctl('e'): /* edit scorefile for group */
/* clear to end of screen */
clear_rest();
s_ref_all = TRUE; /* refresh everything */
sc_score_cmd("e"); /* edit scorefile */
eat_typeahead(); /* stay in control. */
break;
#endif /* SCORE */
case '\t': /* TAB: toggle threadcount display */
sa_mode_desc_threadcount = !sa_mode_desc_threadcount;
s_ref_desc = 0;
break;
case 'a': /* toggle author display */
sa_mode_desc_author = !sa_mode_desc_author;
s_ref_desc = 0;
break;
case '%': /* toggle article # display */
sa_mode_desc_artnum = !sa_mode_desc_artnum;
s_ref_desc = 0;
break;
case 'U': /* toggle unread/unread+read mode */
sa_mode_read_elig = !sa_mode_read_elig;
/* maybe later use the flag to not do this more than once per newsgroup */
for (i = 1; i < sa_num_ents; i++)
s_order_add(i); /* duplicates ignored */
if (sa_eligible(s_first()) || s_next_elig(s_first())) {
#ifdef SCORE
#ifdef PENDING
if (sa_mode_read_elig) {
sc_fill_read = TRUE;
sc_fill_max = absfirst - 1;
}
if (!sa_mode_read_elig)
sc_fill_read = FALSE;
#endif
#endif
s_ref_top = TRUE;
s_rub_ptr();
s_go_top_ents();
s_refill = TRUE;
} else /* quit out: no articles */
return SA_QUIT;
break;
case 'F': /* Fold */
sa_mode_fold = !sa_mode_fold;
s_refill = TRUE;
s_ref_top = TRUE;
break;
case 'f': /* follow */
sa_follow = !sa_follow;
s_ref_top = TRUE;
break;
case 'Z': /* Zero (wipe) selections... */
for (i = 1; i < sa_num_ents; i++)
sa_ents[i].sa_flags = (sa_ents[i].sa_flags & 0xfd);
s_ref_status = 0;
if (!sa_mode_zoom)
break;
s_ref_all = TRUE; /* otherwise won't be refreshed */
/* if in zoom mode, turn it off... */
/* FALL THROUGH */
case 'z': /* zoom mode toggle */
sa_mode_zoom = !sa_mode_zoom;
if (sa_unzoomrefold && !sa_mode_zoom)
sa_mode_fold = TRUE;
/* toggle mode again if no elibible articles left */
if (sa_eligible(s_first()) || s_next_elig(s_first())) {
s_ref_top = TRUE;
s_go_top_ents();
s_refill = TRUE;
} else {
s_beep();
sa_mode_zoom = !sa_mode_zoom; /* toggle it right back */
}
break;
case 'j': /* junk just this article */
(void)sa_art_cmd(FALSE,SA_KILL,a);
/* later refill the page */
s_refill = TRUE;
s_ref_top = TRUE; /* refresh # of articles */
break;
case 'n': /* next art */
case ']':
s_rub_ptr();
if (s_ptr_page_line<(s_bot_ent)) /* more on page... */
s_ptr_page_line +=1;
else {
if (!s_next_elig(page_ents[s_bot_ent].entnum)) {
s_beep();
return 0;
}
s_go_next_page(); /* will jump to top too... */
}
break;
case 'k': /* kill subject thread */
case ',': /* might later clone TRN ',' */
(void)sa_art_cmd(TRUE,SA_KILL,a);
s_refill = TRUE;
s_ref_top = TRUE; /* refresh # of articles */
break;
case Ctl('n'): /* follow subject forward */
i = sa_subj_thread(a);
for (b = s_next_elig(a); b; b = s_next_elig(b))
if (i == sa_subj_thread(b))
break;
if (!b) { /* no next w/ same subject */
s_beep();
return 0;
}
for (i = s_ptr_page_line+1; i <= s_bot_ent; i++)
if (page_ents[i].entnum == b) { /* art is on same page */
s_rub_ptr();
s_ptr_page_line = i;
return 0;
}
/* article is not on page... */
(void)s_fillpage_forward(b); /* fill forward (*must* work) */
s_ptr_page_line = 0;
break;
case Ctl('a'): /* next article with same author... */
/* good for scoring */
b = sa_wrap_next_author(a);
/* rest of code copied from next-subject case above */
if (!b || (a == b)) { /* no next w/ same subject */
s_beep();
return 0;
}
for (i = 0; i <= s_bot_ent; i++)
if (page_ents[i].entnum == b) { /* art is on same page */
s_rub_ptr();
s_ptr_page_line = i;
return 0;
}
/* article is not on page... */
(void)s_fillpage_forward(b); /* fill forward (*must* work) */
s_ptr_page_line = 0;
break;
case Ctl('p'): /* follow subject backwards */
i = sa_subj_thread(a);
for (b = s_prev_elig(a); b; b = s_prev_elig(b))
if (i == sa_subj_thread(b))
break;
if (!b) { /* no next w/ same subject */
s_beep();
return 0;
}
for (i = s_ptr_page_line-1; i >= 0; i--)
if (page_ents[i].entnum == b) { /* art is on same page */
s_rub_ptr();
s_ptr_page_line = i;
return 0;
}
/* article is not on page... */
(void)s_fillpage_backward(b); /* fill backward (*must* work) */
s_ptr_page_line = s_bot_ent; /* go to bottom of page */
(void)s_refillpage(); /* make sure page is full */
s_ref_all = TRUE; /* make everything redrawn... */
break;
case 'G': /* go to article number */
*buf = ' '; /* for finish_cmd() */
if (!s_finish_cmd("Goto article:"))
break;
/* make **sure** that there is a number here */
i = atoi(buf+1);
if (i == 0) { /* it might not be a number */
char* s;
s = buf+1;
if (*s != '0' && ((*s != '+' && *s != '-') || s[1] != '0')) {
/* text was not a numeric 0 */
s_beep();
return 0; /* beep but do nothing */
}
}
sa_art = (ART_NUM)i;
return SA_READ; /* special code to really return */
case 'N': /* next newsgroup */
return SA_NEXT;
case 'P': /* previous newsgroup */
return SA_PRIOR;
case '`':
return SA_QUIT_SEL;
case 'q': /* quit newsgroup */
return SA_QUIT;
case 'Q': /* start reading and quit SA mode */
sa_in = FALSE;
/* FALL THROUGH */
case '\n':
case ' ':
b = sa_readmarked_elig();
if (b) {
sa_clearmark(b);
return b;
}
/* FALL THROUGH */
case 'r': /* read article... */
/* everything needs to be refreshed... */
s_ref_all = TRUE;
return a;
case 'm': /* toggle mark on one article */
case 'M': /* toggle mark on thread */
s_rub_ptr();
(void)sa_art_cmd((*buf == 'M'),SA_MARK,a);
if (!sa_mark_stay) {
/* go to next art on page or top of page if at bottom */
if (s_ptr_page_line < s_bot_ent) /* more on page */
s_ptr_page_line +=1;
else
s_go_top_page(); /* wrap around to top */
}
break;
case 's': /* toggle select1 on one article */
case 'S': /* toggle select1 on a thread */
if (!sa_mode_zoom)
s_rub_ptr();
(void)sa_art_cmd((*buf == 'S'),SA_SELECT,a);
/* if in zoom mode, selection will remove article(s) from the
* page, so that moving the cursor down is unnecessary
*/
if (!sa_mark_stay && !sa_mode_zoom) {
/* go to next art on page or top of page if at bottom */
if (s_ptr_page_line<(s_bot_ent)) /* more on page */
s_ptr_page_line +=1;
else
s_go_top_page(); /* wrap around to top */
}
break;
case 'e': /* extract marked articles */
#if 0
if (!sa_extract_start())
break; /* aborted */
if (!decode_fp)
*decode_dest = '\0'; /* wipe old name */
a = s_first();
if (!s_eligible(a))
a = s_next_elig(a);
flag = FALSE; /* have we found a marked one? */
for ( ; a; a = s_next_elig(a))
if (sa_marked(a)) {
flag = TRUE;
(void)sa_art_cmd(FALSE,SA_EXTRACT,a);
}
if (!flag) { /* none were marked */
a = page_ents[s_ptr_page_line].entnum;
(void)sa_art_cmd(FALSE,SA_EXTRACT,a);
}
s_refill = TRUE;
s_ref_top = TRUE; /* refresh # of articles */
(void)get_anything();
eat_typeahead();
#endif
break;
#if 0
case 'E': /* end extraction, do command on image */
s_ref_all = TRUE;
s_go_bot();
if (decode_fp) {
printf("\nIncomplete file: %s\n",decode_dest) FLUSH;
printf("Continue with command? [ny]");
fflush(stdout);
getcmd(buf);
printf("\n") FLUSH;
if (*buf == 'n' || *buf == ' ' || *buf == '\n')
break;
printf("Remove this file? [ny]");
fflush(stdout);
getcmd(buf);
printf("\n") FLUSH;
if (*buf == 'y' || *buf == 'Y') {
decode_end(); /* will remove file */
break;
}
fclose(decode_fp);
decode_fp = 0;
}
if (!sa_extracted_use) {
sa_extracted_use = safemalloc(LBUFLEN);
/* later consider a variable for the default command */
*sa_extracted_use = '\0';
}
if (!*decode_dest) {
printf("\nTrn doesn't remember an extracted file name.\n") FLUSH;
*buf = ' ';
if (!s_finish_cmd("Please enter a file to use:"))
break;
if (!buf[1]) /* user just typed return */
break;
safecpy(decode_dest,buf+1,MAXFILENAME);
printf("\n") FLUSH;
}
if (sa_extract_dest == NULL) {
sa_extract_dest = (char*)safemalloc(LBUFLEN);
safecpy(sa_extract_dest,filexp("%p"),LBUFLEN);
}
if (*decode_dest != '/' && *decode_dest != '~'
&& *decode_dest != '%') {
sprintf(buf,"%s/%s",sa_extract_dest,decode_dest);
safecpy(decode_dest,buf,MAXFILENAME);
}
if (*sa_extracted_use)
printf("Use command (default %s):\n",sa_extracted_use) FLUSH;
else
printf("Use command (no default):\n") FLUSH;
*buf = ':'; /* cosmetic */
if (!s_finish_cmd(NULL))
break; /* command rubbed out */
if (buf[1] != '\0') /* typed in a command */
safecpy(sa_extracted_use,buf+1,LBUFLEN);
if (*sa_extracted_use == '\0') /* no command */
break;
sprintf(buf,"!%s %s",sa_extracted_use,decode_dest);
printf("\n%s\n",buf+1) FLUSH;
(void)escapade();
(void)get_anything();
eat_typeahead();
break;
#endif
#ifdef SCORE
case '"': /* append to local SCORE file */
s_go_bot();
s_ref_all = TRUE;
printf("Enter score append command or type RETURN for a menu\n");
buf[0] = ':';
buf[1] = FINISHCMD;
if (!finish_command(FALSE))
break;
printf("\n") FLUSH;
sa_go_art(artnum);
sc_append(buf+1);
(void)get_anything();
eat_typeahead();
break;
case '\'': /* execute scoring command */
s_go_bot();
s_ref_all = TRUE;
printf("\nEnter scoring command or type RETURN for a menu\n");
buf[0] = ':';
buf[1] = FINISHCMD;
if (!finish_command(FALSE))
break;
printf("\n") FLUSH;
sa_go_art(artnum);
sc_score_cmd(buf+1);
s_ref_all = TRUE;
(void)get_anything();
eat_typeahead();
break;
#endif
default:
s_beep();
return 0;
} /* switch */
return 0;
}
bool
sa_extract_start()
{
if (sa_extract_dest == NULL) {
sa_extract_dest = (char*)safemalloc(LBUFLEN);
safecpy(sa_extract_dest,filexp("%p"),LBUFLEN);
}
s_go_bot();
printf("To directory (default %s)\n",sa_extract_dest) FLUSH;
*buf = ':'; /* cosmetic */
if (!s_finish_cmd(NULL))
return FALSE; /* command rubbed out */
s_ref_all = TRUE;
/* if the user typed something, copy it to the destination */
if (buf[1] != '\0')
safecpy(sa_extract_dest,filexp(buf+1),LBUFLEN);
/* set a mode for this later? */
printf("\nMark extracted articles as read? [yn]");
fflush(stdout);
getcmd(buf);
printf("\n") FLUSH;
if (*buf == 'y' || *buf == ' ' || *buf == '\n')
sa_extract_junk = TRUE;
else
sa_extract_junk = FALSE;
return TRUE;
}
/* sa_art_cmd primitive: actually does work on an article */
void
sa_art_cmd_prim(cmd,a)
int cmd;
long a;
{
ART_NUM artnum;
artnum = sa_ents[a].artnum;
/* do more onpage status refreshes when in unread+read mode? */
switch (cmd) {
case SA_KILL_MARKED:
if (sa_marked(a)) {
sa_clearmark(a);
oneless_artnum(artnum);
}
break;
case SA_KILL_UNMARKED:
if (sa_marked(a))
break; /* end case early */
oneless_artnum(artnum);
break;
case SA_KILL: /* junk this article */
sa_clearmark(a); /* clearing should be fast */
oneless_artnum(artnum);
break;
case SA_MARK: /* mark this article */
if (sa_marked(a))
sa_clearmark(a);
else
sa_mark(a);
s_ref_status_onpage(a);
break;
case SA_SELECT: /* select this article */
if (sa_selected1(a)) {
sa_clearselect1(a);
if (sa_mode_zoom)
s_refill = TRUE; /* article is now ineligible */
} else
sa_select1(a);
s_ref_status_onpage(a);
break;
case SA_EXTRACT:
sa_clearmark(a);
art = artnum;
*buf = 'e'; /* fake up the extract command */
safecpy(buf+1,sa_extract_dest,LBUFLEN);
(void)save_article();
if (sa_extract_junk)
oneless_artnum(artnum);
break;
} /* switch */
}
/* return value is unused for now, but may be later... */
/* note: refilling after a kill is the caller's responsibility */
int
sa_art_cmd(multiple,cmd,a)
int multiple; /* follow the thread? */
int cmd; /* what to do */
long a; /* article # to affect or start with */
{
long b;
sa_art_cmd_prim(cmd,a); /* do the first article */
if (!multiple)
return 0; /* no problem... */
b = a;
while ((b = sa_subj_thread_next(b)) != 0)
/* if this is basically eligible and the same subject thread# */
sa_art_cmd_prim(cmd,b);
return 0;
}
/* XXX this needs a good long thinking session before re-activating */
long
sa_wrap_next_author(a)
long a;
{
#ifdef UNDEF
long b;
char* s;
char* s2;
s = (char*)sa_desc_author(a,20); /* 20 characters should be enough */
for (b = s_next_elig(a); b; b = s_next_elig(b))
if (STRSTR(get_from_line(b),s))
break; /* out of the for loop */
if (b) /* found it */
return b;
/* search from first article (maybe return original art) */
b = s_first();
if (!sa_eligible(b))
b = s_next_elig(b);
for ( ; b; b = s_next_elig(b))
if (STRSTR(get_from_line(b),s))
break; /* out of the for loop */
return b;
#else
return a; /* feature is disabled */
#endif
}
#endif /* SCAN */
syntax highlighted by Code2HTML, v. 0.9.1