/* rt-select.c */ /* This software is copyrighted as detailed in the LICENSE file. */ #include "EXTERN.h" #include "common.h" #include "list.h" #include "trn.h" #include "term.h" #include "final.h" #include "util.h" #include "util2.h" #include "help.h" #include "hash.h" #include "cache.h" #include "bits.h" #include "artsrch.h" #include "ng.h" #include "opt.h" #include "ngdata.h" #include "nntpclient.h" #include "datasrc.h" #include "addng.h" #include "nntp.h" #include "ngstuff.h" #include "ngsrch.h" #include "rcstuff.h" #include "rcln.h" #include "kfile.h" #include "intrp.h" #include "search.h" #include "rthread.h" #include "univ.h" #include "rt-page.h" #include "rt-util.h" #include "color.h" #include "only.h" #ifdef USE_TK #include "tkstuff.h" #include "tktree.h" #endif #include "INTERN.h" #include "rt-select.h" #include "rt-select.ih" static char sel_ret; static char page_char, end_char; static int disp_status_line; static bool clean_screen; static int removed_prompt; static int force_sel_pos; #define START_SELECTOR(new_mode)\ char save_mode = mode;\ char save_gmode = gmode;\ bos_on_stop = TRUE;\ set_mode('s',new_mode) #define END_SELECTOR()\ bos_on_stop = FALSE;\ set_mode(save_gmode,save_mode) #define PUSH_SELECTOR()\ int save_sel_mode = sel_mode;\ bool save_sel_rereading = sel_rereading;\ bool save_sel_exclusive = sel_exclusive;\ ART_UNREAD save_selected_count = selected_count;\ int (*save_extra_commands) _((char_int)) = extra_commands #define POP_SELECTOR()\ sel_exclusive = save_sel_exclusive;\ sel_rereading = save_sel_rereading;\ selected_count = save_selected_count;\ extra_commands = save_extra_commands;\ bos_on_stop = TRUE;\ if (sel_mode != save_sel_mode) {\ sel_mode = save_sel_mode;\ set_selector(0, 0);\ save_sel_mode = 0;\ } #define PUSH_UNIV_SELECTOR()\ UNIV_ITEM* save_first_univ = first_univ;\ UNIV_ITEM* save_last_univ = last_univ;\ UNIV_ITEM* save_page_univ = sel_page_univ;\ UNIV_ITEM* save_next_univ = sel_next_univ;\ char* save_univ_fname = univ_fname;\ char* save_univ_label = univ_label;\ char* save_univ_title = univ_title;\ char* save_univ_tmp_file = univ_tmp_file;\ char save_sel_ret = sel_ret;\ HASHTABLE* save_univ_ng_hash = univ_ng_hash;\ HASHTABLE* save_univ_vg_hash = univ_vg_hash #define POP_UNIV_SELECTOR()\ first_univ = save_first_univ;\ last_univ = save_last_univ;\ sel_page_univ = save_page_univ;\ sel_next_univ = save_next_univ;\ univ_fname = save_univ_fname;\ univ_label = save_univ_label;\ univ_title = save_univ_title;\ univ_tmp_file = save_univ_tmp_file;\ sel_ret = save_sel_ret;\ univ_ng_hash = save_univ_ng_hash;\ univ_vg_hash = save_univ_vg_hash static int (*extra_commands) _((char_int)); /* Display a menu of threads/subjects/articles for the user to choose from. ** If "cmd" is '+' we display all the unread items and allow the user to mark ** them as selected and perform various commands upon them. If "cmd" is 'U' ** the list consists of previously-read items for the user to mark as unread. */ char article_selector(cmd) char_int cmd; { bool save_selected_only; START_SELECTOR('t'); sel_rereading = (cmd == 'U'); art = lastart+1; extra_commands = article_commands; keep_the_group_static = (keep_the_group_static == 1); sel_mode = SM_ARTICLE; set_sel_mode(cmd); if (!cache_range(sel_rereading? absfirst : firstart, lastart)) { sel_ret = '+'; goto sel_exit; } sel_restart: /* Setup for selecting articles to read or set unread */ if (sel_rereading) { end_char = 'Z'; page_char = '>'; sel_page_app = NULL; sel_page_sp = NULL; sel_mask = AF_DELSEL; } else { end_char = NewsSelCmds[0]; page_char = NewsSelCmds[1]; if (curr_artp) { sel_last_ap = curr_artp; sel_last_sp = curr_artp->subj; } sel_mask = AF_SEL; } save_selected_only = selected_only; selected_only = TRUE; count_subjects(cmd? CS_UNSEL_STORE : CS_NORM); init_pages(FILL_LAST_PAGE); sel_item_index = 0; *msg = '\0'; if (added_articles) { register long i = added_articles, j; for (j = lastart-i+1; j <= lastart; j++) { if (!article_unread(j)) i--; } if (i == added_articles) sprintf(msg, "** %ld new article%s arrived ** ", (long)added_articles, PLURAL(added_articles)); else sprintf(msg, "** %ld of %ld new articles unread ** ", i, (long)added_articles); disp_status_line = 1; } added_articles = 0; if (cmd && selected_count) { sprintf(msg+strlen(msg), "%ld article%s selected.", (long)selected_count, selected_count == 1? " is" : "s are"); disp_status_line = 1; } cmd = 0; sel_display(); if (sel_input() == 'R') goto sel_restart; sel_cleanup(); newline(); if (mousebar_cnt) clear_rest(); sel_exit: if (sel_ret == '\033') sel_ret = '+'; else if (sel_ret == '`') sel_ret = 'Q'; if (sel_rereading) { sel_rereading = FALSE; sel_mask = AF_SEL; } if (sel_mode != SM_ARTICLE || sel_sort == SS_GROUPS || sel_sort == SS_STRING) { if (artptr_list) { free((char*)artptr_list); artptr_list = sel_page_app = NULL; sort_subjects(); } artptr = NULL; #ifdef ARTSEARCH if (!ThreadedGroup) srchahead = -1; #endif } #ifdef ARTSEARCH else srchahead = 0; #endif selected_only = (selected_count != 0); if (sel_ret == '+') { selected_only = save_selected_only; count_subjects(CS_RESELECT); } else count_subjects(CS_UNSELECT); if (sel_ret == '+') { art = curr_art; artp = curr_artp; } else top_article(); END_SELECTOR(); return sel_ret; } static void sel_dogroups() { NGDATA* np; int ret; int save_selected_count = selected_count; for (np = first_ng; np; np = np->next) { if (!(np->flags & NF_VISIT)) continue; do_group: if (np->flags & NF_SEL) { np->flags &= ~NF_SEL; save_selected_count--; } set_ng(np); if (np != current_ng) { recent_ng = current_ng; current_ng = np; } ThreadedGroup = (use_threads && !(np->flags & NF_UNTHREADED)); printf("Entering %s:", ngname); #ifdef SCAN_ART if (sel_ret == ';') { ret = do_newsgroup(savestr(";")); } else #endif ret = do_newsgroup(nullstr); switch (ret) { case NG_NORM: case NG_SELNEXT: set_ng(np->next); break; case NG_NEXT: set_ng(np->next); goto loop_break; case NG_ERROR: case NG_ASK: goto loop_break; case NG_SELPRIOR: while ((np = np->prev) != NULL) { if (np->flags & NF_VISIT) goto do_group; } (void) first_page(); goto loop_break; case NG_MINUS: np = recent_ng; #if 0 /* CAA: I'm not sure why I wrote this originally--it seems unnecessary */ np->flags |= NF_VISIT; #endif goto do_group; #ifdef SUPPORT_NNTP case NG_NOSERVER: nntp_server_died(np->rc->datasrc); (void) first_page(); break; #endif /* CAA extensions */ case NG_GO_ARTICLE: np = ng_go_ngptr; goto do_group; /* later: possible go-to-newsgroup (applicable?) */ } } loop_break: selected_count = save_selected_count; } char multirc_selector() { START_SELECTOR('c'); sel_rereading = FALSE; sel_exclusive = FALSE; selected_count = 0; set_selector(SM_MULTIRC, 0); sel_restart: end_char = NewsrcSelCmds[0]; page_char = NewsrcSelCmds[1]; sel_mask = MF_SEL; extra_commands = multirc_commands; init_pages(FILL_LAST_PAGE); sel_item_index = 0; sel_display(); if (sel_input() == 'R') goto sel_restart; newline(); if (mousebar_cnt) clear_rest(); if (sel_ret=='\r' || sel_ret=='\n' || sel_ret=='Z' || sel_ret=='\t') { MULTIRC* mp; NEWSRC* rp; PUSH_SELECTOR(); for (mp = multirc_low(); mp; mp = multirc_next(mp)) { if (mp->flags & MF_SEL) { mp->flags &= ~MF_SEL; save_selected_count--; for (rp = mp->first; rp; rp = rp->next) rp->datasrc->flags &= ~DF_UNAVAILABLE; if (use_multirc(mp)) { find_new_groups(); do_multirc(); unuse_multirc(mp); } else mp->flags &= ~MF_INCLUDED; } } POP_SELECTOR(); goto sel_restart; } END_SELECTOR(); return sel_ret; } char newsgroup_selector() { START_SELECTOR('w'); sel_rereading = FALSE; sel_exclusive = FALSE; selected_count = 0; set_selector(SM_NEWSGROUP, 0); sel_restart: if (*sel_grp_dmode != 's') { NEWSRC* rp; for (rp = multirc->first; rp; rp = rp->next) { if ((rp->flags & RF_ACTIVE) && !rp->datasrc->desc_sf.hp) { find_grpdesc(rp->datasrc, "control"); #ifdef SUPPORT_NNTP if (rp->datasrc->desc_sf.fp) rp->datasrc->flags |= DF_NOXGTITLE; /*$$ ok?*/ else rp->datasrc->desc_sf.refetch_secs = 0; #endif } } } end_char = NewsgroupSelCmds[0]; page_char = NewsgroupSelCmds[1]; if (sel_rereading) { sel_mask = NF_DELSEL; sel_page_np = NULL; } else sel_mask = NF_SEL; extra_commands = newsgroup_commands; init_pages(FILL_LAST_PAGE); sel_item_index = 0; sel_display(); if (sel_input() == 'R') goto sel_restart; newline(); if (mousebar_cnt) clear_rest(); if (sel_ret=='\r' || sel_ret=='\n' || sel_ret=='Z' || sel_ret=='\t' #ifdef SCAN_ART || sel_ret==';' #endif ) { NGDATA* np; PUSH_SELECTOR(); for (np = first_ng; np; np = np->next) { if ((np->flags & NF_INCLUDED) && (!selected_count || (np->flags & sel_mask))) np->flags |= NF_VISIT; else np->flags &= ~NF_VISIT; } sel_dogroups(); save_selected_count = selected_count; POP_SELECTOR(); if (multirc) goto sel_restart; sel_ret = 'q'; } sel_cleanup(); END_SELECTOR(); end_only(); return sel_ret; } char addgroup_selector(flags) int flags; { START_SELECTOR('j'); sel_rereading = FALSE; sel_exclusive = FALSE; selected_count = 0; set_selector(SM_ADDGROUP, 0); sel_restart: if (*sel_grp_dmode != 's') { NEWSRC* rp; for (rp = multirc->first; rp; rp = rp->next) { if ((rp->flags & RF_ACTIVE) && !rp->datasrc->desc_sf.hp) { find_grpdesc(rp->datasrc, "control"); #ifdef SUPPORT_NNTP if (!rp->datasrc->desc_sf.fp) rp->datasrc->desc_sf.refetch_secs = 0; #endif } } } end_char = AddSelCmds[0]; page_char = AddSelCmds[1]; /* Setup for selecting articles to read or set unread */ if (sel_rereading) sel_mask = AGF_DELSEL; else sel_mask = AGF_SEL; sel_page_gp = NULL; extra_commands = addgroup_commands; init_pages(FILL_LAST_PAGE); sel_item_index = 0; sel_display(); if (sel_input() == 'R') goto sel_restart; selected_count = 0; newline(); if (mousebar_cnt) clear_rest(); if (sel_ret=='\r' || sel_ret=='\n' || sel_ret=='Z' || sel_ret=='\t') { ADDGROUP *gp; int i; addnewbydefault = ADDNEW_SUB; for (gp = first_addgroup, i = 0; gp; gp = gp->next, i++) { if (gp->flags & NF_SEL) { gp->flags &= ~NF_SEL; get_ng(gp->name,flags); } } addnewbydefault = 0; } sel_cleanup(); END_SELECTOR(); return sel_ret; } char option_selector() { int i; char** vals = INI_VALUES(options_ini); START_SELECTOR('l'); sel_rereading = FALSE; sel_exclusive = FALSE; selected_count = 0; parse_ini_section(nullstr, options_ini); set_selector(SM_OPTIONS, 0); sel_restart: end_char = OptionSelCmds[0]; page_char = OptionSelCmds[1]; if (sel_rereading) sel_mask = AF_DELSEL; else sel_mask = AF_SEL; sel_page_op = -1; extra_commands = option_commands; init_pages(FILL_LAST_PAGE); sel_item_index = 0; sel_display(); if (sel_input() == 'R' || sel_ret=='\r' || sel_ret=='\n') goto sel_restart; selected_count = 0; newline(); if (mousebar_cnt) clear_rest(); if (sel_ret=='Z' || sel_ret=='\t' || sel_ret == 'S') { set_options(vals); if (sel_ret == 'S') save_options(ini_file); } for (i = 1; options_ini[i].checksum; i++) { if (vals[i]) { if (option_saved_vals[i] && strEQ(vals[i],option_saved_vals[i])) { if (option_saved_vals[i] != option_def_vals[i]) free(option_saved_vals[i]); option_saved_vals[i] = NULL; } free(vals[i]); vals[i] = NULL; } } END_SELECTOR(); return sel_ret; } /* returns a command to do */ static int univ_read(ui) UNIV_ITEM* ui; { int exit_code = UR_NORM; char ch; univ_follow_temp = FALSE; if (!ui) { printf("NULL UI passed to reader!\n") FLUSH; sleep(5); return exit_code; } printf("\n") FLUSH; /* prepare for output msgs... */ switch (ui->type) { case UN_DEBUG1: { char* s; s = ui->data.str; if (s && *s) { printf("Not implemented yet (%s)\n",s) FLUSH; sleep(5); return exit_code; } break; } case UN_TEXTFILE: { char* s; s = ui->data.textfile.fname; if (s && *s) { /* later have some way of getting a return code back */ univ_page_file(s); } break; } case UN_ARTICLE: { int ret; NGDATA* np; if (in_ng) { /* XXX whine: can't recurse at this time */ break; } if (!ui->data.virt.ng) break; /* XXX whine */ np = find_ng(ui->data.virt.ng); if (!np) { printf("Universal: newsgroup %s not found!", ui->data.virt.ng) FLUSH; sleep(5); return exit_code; } set_ng(np); if (np != current_ng) { recent_ng = current_ng; current_ng = np; } ThreadedGroup = (use_threads && !(np->flags & NF_UNTHREADED)); printf("Virtual: Entering %s:\n", ngname) FLUSH; ng_go_artnum = ui->data.virt.num; univ_read_virtflag = TRUE; ret = do_newsgroup(nullstr); univ_read_virtflag = FALSE; switch (ret) { case NG_NORM: /* handle more cases later */ case NG_SELNEXT: case NG_NEXT: /* just continue reading */ break; case NG_SELPRIOR: /* not implemented yet */ /* FALL THROUGH */ case NG_ERROR: case NG_ASK: exit_code = UR_BREAK; return exit_code; case NG_MINUS: /* not implemented */ break; default: break; } break; } case UN_GROUPMASK: { univ_mask_load(ui->data.gmask.masklist,ui->data.gmask.title); ch = universal_selector(); switch (ch) { case 'q': exit_code = UR_BREAK; break; default: exit_code = UR_NORM; break; } return exit_code; } case UN_CONFIGFILE: { univ_file_load(ui->data.cfile.fname,ui->data.cfile.title, ui->data.cfile.label); ch = universal_selector(); switch (ch) { case 'q': exit_code = UR_BREAK; break; default: exit_code = UR_NORM; break; } return exit_code; } case UN_NEWSGROUP: { int ret; NGDATA* np; if (in_ng) { /* XXX whine: can't recurse at this time */ break; } if (!ui->data.group.ng) break; /* XXX whine */ np = find_ng(ui->data.group.ng); if (!np) { printf("Universal: newsgroup %s not found!", ui->data.group.ng) FLUSH; sleep(5); return exit_code; } do_group: set_ng(np); if (np != current_ng) { recent_ng = current_ng; current_ng = np; } ThreadedGroup = (use_threads && !(np->flags & NF_UNTHREADED)); printf("Entering %s:", ngname) FLUSH; #ifdef SCAN_ART if (sel_ret == ';') ret = do_newsgroup(savestr(";")); else #endif ret = do_newsgroup(nullstr); switch (ret) { case NG_NORM: /* handle more cases later */ case NG_SELNEXT: case NG_NEXT: /* just continue reading */ break; case NG_SELPRIOR: /* not implemented yet */ /* FALL THROUGH */ case NG_ERROR: case NG_ASK: exit_code = UR_BREAK; return exit_code; case NG_MINUS: np = recent_ng; goto do_group; #ifdef SUPPORT_NNTP case NG_NOSERVER: /* Eeep! */ break; #endif } break; } case UN_HELPKEY: if (another_command(univ_key_help(ui->data.i))) pushchar(sel_ret | 0200); break; default: break; } return exit_code; } char universal_selector() { START_SELECTOR('v'); /* kind of like 'v'irtual... */ sel_rereading = FALSE; sel_exclusive = FALSE; selected_count = 0; set_selector(SM_UNIVERSAL, 0); selected_count = 0; sel_restart: /* make options */ end_char = 'Z'; page_char = '>'; /* Setup for selecting articles to read or set unread */ if (sel_rereading) sel_mask = UF_DELSEL; else sel_mask = UF_SEL; sel_page_univ = NULL; extra_commands = universal_commands; init_pages(FILL_LAST_PAGE); sel_item_index = 0; sel_display(); if (sel_input() == 'R') goto sel_restart; newline(); if (mousebar_cnt) clear_rest(); if (sel_ret=='\r' || sel_ret=='\n' || sel_ret=='\t' #ifdef SCAN_ART || sel_ret==';' #endif || sel_ret=='Z') { UNIV_ITEM *ui; int i; for (ui = first_univ, i = 0; ui; ui = ui->next, i++) { int ret; if (ui->flags & UF_SEL) { PUSH_SELECTOR(); PUSH_UNIV_SELECTOR(); ui->flags &= ~UF_SEL; save_selected_count--; ret = univ_read(ui); POP_UNIV_SELECTOR(); POP_SELECTOR(); if (ret == UR_ERROR || ret == UR_BREAK) { sel_ret = ' '; /* don't leave completely. */ break; /* jump out of for loop */ } } } } /*univ_loop_break:*/ /* restart the selector unless the user explicitly quits. * possibly later have an option for 'Z' to quit levels>1. */ if (sel_ret != 'q' && (sel_ret != 'Q')) goto sel_restart; sel_cleanup(); univ_close(); END_SELECTOR(); return sel_ret; } static void sel_display() { /* Present a page of items to the user */ display_page(); if (erase_screen && erase_each_line) erase_line(1); if (sel_item_index >= sel_page_item_cnt) sel_item_index = 0; if (disp_status_line == 1) { newline(); fputs(msg, stdout); term_col = strlen(msg); disp_status_line = 2; } } static void sel_status_msg(cp) char* cp; { if (can_home) goto_xy(0,sel_last_line+1); else newline(); fputs(cp, stdout); term_col = strlen(cp); goto_xy(0,sel_items[sel_item_index].line); fflush(stdout); /* CAA: otherwise may not be visible */ disp_status_line = 2; } static char sel_input() { register int j; int ch, action; char* in_select; int got_dash, got_goto; int sel_number; int ch_num1; /* CAA: TRN proudly continues the state machine traditions of RN. * April 2, 1996: 28 gotos in this function. Conversion to * structured programming is left as an exercise for the reader. */ /* If one immediately types a goto command followed by a dash ('-'), * the following will be the default action. */ action = '+'; reask_selector: /* Prompt the user */ sel_prompt(); position_selector: got_dash = got_goto = 0; force_sel_pos = -1; if (removed_prompt & 1) { draw_mousebar(COLS,0); removed_prompt &= ~1; } if (can_home) goto_xy(0,sel_items[sel_item_index].line); #ifdef USE_TK /* Allow Tk to do something with the current positioning */ if (ttk_running) { SEL_UNION u; u = sel_items[sel_item_index].u; switch (sel_mode) { case SM_THREAD: case SM_SUBJECT: ttcl_eval("wipetree"); if (sel_page_item_cnt != 0) ttk_draw_tree(u.sp->thread, 0, 0); break; case SM_ARTICLE: case SM_MULTIRC: case SM_ADDGROUP: case SM_NEWSGROUP: case SM_OPTIONS: case SM_UNIVERSAL: default: break; } } #endif reinp_selector: if (removed_prompt & 1) goto position_selector; /* (CAA: TRN considered harmful? :-) */ /* Grab some commands from the user */ fflush(stdout); eat_typeahead(); if (UseSelNum) spin_char = '0' + (sel_item_index+1)/10; /* first digit */ else spin_char = sel_chars[sel_item_index]; cache_until_key(); getcmd(buf); if (*buf == ' ') setdef(buf, sel_at_end? &end_char : &page_char); ch = *buf; if (errno) ch = Ctl('l'); if (disp_status_line == 2) { if (can_home) { goto_xy(0,sel_last_line+1); erase_line(0); if (term_line == LINES-1) removed_prompt |= 1; } disp_status_line = 0; } if (ch == '-' && sel_page_item_cnt) { got_dash = 1; got_goto = 0; /* right action is not clear if both are true */ if (can_home) { if (!input_pending()) { j = (sel_item_index > 0? sel_item_index : sel_page_item_cnt); if (UseSelNum) sprintf(msg,"Range: %d-", j); else sprintf(msg,"Range: %c-", sel_chars[j-1]); sel_status_msg(msg); } } else { putchar('-'); fflush(stdout); } goto reinp_selector; } /* allow the user to back out of a range or a goto with erase char */ if (ch == ERASECH || ch == KILLCH) { /* later consider dingaling() if neither got_{dash,goto} is true */ got_dash = 0; got_goto = 0; /* following if statement should be function */ if (disp_status_line == 2) { /* status was printed */ if (can_home) { goto_xy(0,sel_last_line+1); erase_line(0); if (term_line == LINES-1) removed_prompt |= 1; } disp_status_line = 0; } goto position_selector; } if (ch == Ctl('g')) { got_goto = 1; got_dash = 0; /* right action is not clear if both are true */ if (!input_pending()) { if (UseSelNum) sel_status_msg("Go to number?"); else sel_status_msg("Go to letter?"); } goto reinp_selector; } if (sel_mode == SM_OPTIONS && (ch == '\r' || ch == '\n')) ch = '.'; in_select = index(sel_chars, ch); if (UseSelNum && ch >= '0' && ch <= '9') { ch_num1 = ch; /* would be *very* nice to use wait_key_pause() here */ if (!input_pending()) { if (got_dash) { if (sel_item_index > 0) { j = sel_item_index; /* -1, +1 to print */ } else /* wrap around from the bottom */ j = sel_page_item_cnt; sprintf(msg,"Range: %d-%c", j, ch); } else { if (got_goto) { sprintf(msg,"Go to number: %c", ch); } else { sprintf(msg,"%c", ch); } } sel_status_msg(msg); } /* Consider cache_until_key() here. The middle of typing a * number is a lousy time to delay, however. */ getcmd(buf); ch = *buf; if (disp_status_line == 2) { /* status was printed */ if (can_home) { goto_xy(0,sel_last_line+1); erase_line(0); if (term_line == LINES-1) removed_prompt |= 1; } disp_status_line = 0; } if (ch == KILLCH) { /* kill whole command in progress */ got_goto = 0; got_dash = 0; goto position_selector; } if (ch == ERASECH) { /* Erase any first digit printed, but allow complex * commands to continue. Spaces at end of message are * there to wipe out old first digit. */ if (got_dash) { if (sel_item_index > 0) { j = sel_item_index; /* -1, +1 to print */ } else /* wrap around from the bottom */ j = sel_page_item_cnt; sprintf(msg,"Range: %d- ", j); sel_status_msg(msg); goto reinp_selector; } if (got_goto) { sel_status_msg("Go to number? "); goto reinp_selector; } goto position_selector; } if (ch >= '0' && ch <= '9') sel_number = ((ch_num1 - '0') * 10) + (ch - '0'); else { pushchar(ch); /* for later use */ sel_number = (ch_num1 - '0'); } j = sel_number-1; if ((j < 0) || (j >= sel_page_item_cnt)) { dingaling(); sprintf(msg, "No item %c%c on this page.", ch_num1, ch); sel_status_msg(msg); goto position_selector; } else if (got_goto || (SelNumGoto && !got_dash)) { /* (but only do always-goto if there was not a dash) */ sel_item_index = j; goto position_selector; } else if (got_dash) ; else if (sel_items[j].sel == 1) action = (sel_rereading? 'k' : '-'); else action = '+'; } else if (in_select && !UseSelNum) { j = in_select - sel_chars; if (j >= sel_page_item_cnt) { dingaling(); sprintf(msg, "No item '%c' on this page.", ch); sel_status_msg(msg); goto position_selector; } else if (got_goto) { sel_item_index = j; goto position_selector; } else if (got_dash) ; else if (sel_items[j].sel == 1) action = (sel_rereading? 'k' : '-'); else action = '+'; } else if (ch == '*' && sel_mode == SM_ARTICLE) { register ARTICLE* ap; if (!sel_page_item_cnt) dingaling(); else { ap = sel_items[sel_item_index].u.ap; if (sel_items[sel_item_index].sel) deselect_subject(ap->subj); else select_subject(ap->subj, 0); update_page(); } goto position_selector; } else if (ch == 'y' || ch == '.' || ch == '*' || ch == Ctl('t')) { j = sel_item_index; if (sel_page_item_cnt && sel_items[j].sel == 1) action = (sel_rereading? 'k' : '-'); else action = '+'; if (ch == Ctl('t')) force_sel_pos = j; } else if (ch == 'k' || ch == 'j' || ch == ',') { j = sel_item_index; action = 'k'; } else if (ch == 'm' || ch == '|') { j = sel_item_index; action = '-'; } else if (ch == 'M' && in_ng) { j = sel_item_index; action = 'M'; } else if (ch == '@') { sel_item_index = 0; j = sel_page_item_cnt-1; got_dash = 1; action = '@'; } else if (ch == '[' || ch == 'p') { if (--sel_item_index < 0) sel_item_index = sel_page_item_cnt? sel_page_item_cnt-1 : 0; goto position_selector; } else if (ch == ']' || ch == 'n') { if (++sel_item_index >= sel_page_item_cnt) sel_item_index = 0; goto position_selector; } else { sel_ret = ch; switch (sel_command(ch)) { case DS_ASK: if (!clean_screen) { sel_display(); goto reask_selector; } if (removed_prompt & 2) { carriage_return(); goto reask_selector; } goto position_selector; case DS_DISPLAY: sel_display(); goto reask_selector; case DS_UPDATE: if (!clean_screen) { sel_display(); goto reask_selector; } if (disp_status_line == 1) { newline(); fputs(msg,stdout); term_col = strlen(msg); if (removed_prompt & 1) { draw_mousebar(COLS,0); removed_prompt &= ~1; } disp_status_line = 2; } update_page(); if (can_home) goto_xy(0,sel_last_line); goto reask_selector; case DS_RESTART: return 'R'; /*Restart*/ case DS_QUIT: return 'Q'; /*Quit*/ case DS_STATUS: disp_status_line = 1; if (!clean_screen) { sel_display(); goto reask_selector; } sel_status_msg(msg); if (!can_home) newline(); if (removed_prompt & 2) goto reask_selector; goto position_selector; } } if (!sel_page_item_cnt) { dingaling(); goto position_selector; } /* The user manipulated one of the letters -- handle it. */ if (!got_dash) sel_item_index = j; else { if (j < sel_item_index) { ch = sel_item_index-1; sel_item_index = j; j = ch; } } if (++j == sel_page_item_cnt) j = 0; do { register int sel = sel_items[sel_item_index].sel; if (can_home) goto_xy(0,sel_items[sel_item_index].line); if (action == '@') { if (sel == 2) ch = (sel_rereading? '+' : ' '); else if (sel_rereading) ch = 'k'; else if (sel == 1) ch = '-'; else ch = '+'; } else ch = action; switch (ch) { case '+': if (select_item(sel_items[sel_item_index].u)) output_sel(sel_item_index, 1, TRUE); if (term_line >= sel_last_line) { sel_display(); goto reask_selector; } break; case '-': case 'k': case 'M': { bool sel_reread_save = sel_rereading; if (ch == 'M') delay_return_item(sel_items[sel_item_index].u); if (ch == '-') sel_rereading = FALSE; else sel_rereading = TRUE; if (deselect_item(sel_items[sel_item_index].u)) output_sel(sel_item_index, ch == '-' ? 0 : 2, TRUE); sel_rereading = sel_reread_save; if (term_line >= sel_last_line) { sel_display(); goto reask_selector; } break; } } if (can_home) carriage_return(); fflush(stdout); if (++sel_item_index == sel_page_item_cnt) sel_item_index = 0; } while (sel_item_index != j); if (force_sel_pos >= 0) sel_item_index = force_sel_pos; goto position_selector; } static void sel_prompt() { draw_mousebar(COLS,0); if (can_home) goto_xy(0,sel_last_line); #ifdef MAILCALL setmail(FALSE); #endif if (sel_at_end) sprintf(cmd_buf, "%s [%c%c] --", (!sel_prior_obj_cnt? "All" : "Bot"), end_char, page_char); else sprintf(cmd_buf, "%s%ld%% [%c%c] --", (!sel_prior_obj_cnt? "Top " : nullstr), (long)((sel_prior_obj_cnt+sel_page_obj_cnt)*100 / sel_total_obj_cnt), page_char, end_char); interp(buf, sizeof buf, mailcall); sprintf(msg, "%s-- %s %s (%s%s order) -- %s", buf, sel_exclusive && in_ng? "SELECTED" : "Select", sel_mode_string, sel_direction<0? "reverse " : nullstr, sel_sort_string, cmd_buf); color_string(COLOR_CMD,msg); term_col = strlen(msg); removed_prompt = 0; } static bool select_item(u) SEL_UNION u; { switch (sel_mode) { case SM_MULTIRC: if (!(u.mp->flags & sel_mask)) selected_count++; u.mp->flags = (u.mp->flags /*& ~MF_DEL*/) | sel_mask; break; case SM_ADDGROUP: if (!(u.gp->flags & sel_mask)) selected_count++; u.gp->flags = (u.gp->flags & ~AGF_DEL) | sel_mask; break; case SM_NEWSGROUP: if (!(u.np->flags & sel_mask)) selected_count++; u.np->flags = (u.np->flags & ~NF_DEL) | sel_mask; break; case SM_OPTIONS: if (!select_option(u.op) || !INI_VALUE(options_ini,u.op)) return FALSE; break; case SM_THREAD: select_thread(u.sp->thread, 0); break; case SM_SUBJECT: select_subject(u.sp, 0); break; case SM_ARTICLE: select_article(u.ap, 0); break; case SM_UNIVERSAL: if (!(u.un->flags & sel_mask)) selected_count++; u.un->flags = (u.un->flags & ~UF_DEL) | sel_mask; break; } return TRUE; } static bool delay_return_item(u) SEL_UNION u; { switch (sel_mode) { case SM_MULTIRC: case SM_ADDGROUP: case SM_NEWSGROUP: case SM_OPTIONS: case SM_UNIVERSAL: return FALSE; case SM_ARTICLE: delay_unmark(u.ap); break; default: { register ARTICLE* ap; if (sel_mode == SM_THREAD) { for (ap = first_art(u.sp); ap; ap = next_art(ap)) if (!!(ap->flags & AF_UNREAD) ^ sel_rereading) delay_unmark(ap); } else { for (ap = u.sp->articles; ap; ap = ap->subj_next) if (!!(ap->flags & AF_UNREAD) ^ sel_rereading) delay_unmark(ap); } break; } } return TRUE; } static bool deselect_item(u) SEL_UNION u; { switch (sel_mode) { case SM_MULTIRC: if (u.mp->flags & sel_mask) { u.mp->flags &= ~sel_mask; selected_count--; } #if 0 if (sel_rereading) u.mp->flags |= MF_DEL; #endif break; case SM_ADDGROUP: if (u.gp->flags & sel_mask) { u.gp->flags &= ~sel_mask; selected_count--; } if (sel_rereading) u.gp->flags |= AGF_DEL; break; case SM_NEWSGROUP: if (u.np->flags & sel_mask) { u.np->flags &= ~sel_mask; selected_count--; } if (sel_rereading) u.np->flags |= NF_DEL; break; case SM_OPTIONS: if (!select_option(u.op) || INI_VALUE(options_ini,u.op)) return FALSE; break; case SM_THREAD: deselect_thread(u.sp->thread); break; case SM_SUBJECT: deselect_subject(u.sp); break; case SM_UNIVERSAL: if (u.un->flags & sel_mask) { u.un->flags &= ~sel_mask; selected_count--; } if (sel_rereading) u.un->flags |= UF_DEL; break; default: deselect_article(u.ap, 0); break; } return TRUE; } static bool select_option(i) int i; { bool changed = FALSE; char** vals = INI_VALUES(options_ini); char* val; char* oldval; if (*options_ini[i].item == '*') { option_flags[i] ^= OF_SEL; init_pages(FILL_LAST_PAGE); term_line = sel_last_line; return FALSE; } goto_xy(0,sel_last_line); erase_line(mousebar_cnt > 0); /* erase the prompt */ color_object(COLOR_CMD, 1); printf("Change `%s' (%s)",options_ini[i].item, options_ini[i].help_str); color_pop(); /* of COLOR_CMD */ newline(); *buf = '\0'; oldval = savestr(quote_string(option_value(i))); val = vals[i]? vals[i] : oldval; clean_screen = in_choice("> ", val, options_ini[i].help_str, 'z'); if (strNE(buf,val)) { char* to = buf; char* from = buf; parse_string(&to, &from); changed = TRUE; if (vals[i]) { free(vals[i]); selected_count--; } if (val != oldval && strEQ(buf,oldval)) vals[i] = NULL; else { vals[i] = savestr(buf); selected_count++; } } free(oldval); if (clean_screen) { up_line(); erase_line(1); sel_prompt(); goto_xy(0,sel_items[sel_item_index].line); if (changed) { erase_line(0); display_option(i,sel_item_index); up_line(); } } else return FALSE; return TRUE; } static void sel_cleanup() { switch (sel_mode) { case SM_MULTIRC: break; case SM_ADDGROUP: if (sel_rereading) { ADDGROUP* gp; for (gp = first_addgroup; gp; gp = gp->next) { if (gp->flags & AGF_DELSEL) { if (!(gp->flags & AGF_SEL)) selected_count++; gp->flags = (gp->flags&~(AGF_DELSEL|AGF_EXCLUDED))|AGF_SEL; } gp->flags &= ~AGF_DEL; } } else { ADDGROUP* gp; for (gp = first_addgroup; gp; gp = gp->next) { if (gp->flags & AGF_DEL) gp->flags = (gp->flags & ~AGF_DEL) | AGF_EXCLUDED; } } break; case SM_NEWSGROUP: if (sel_rereading) { NGDATA* np; for (np = first_ng; np; np = np->next) { if (np->flags & NF_DELSEL) { if (!(np->flags & NF_SEL)) selected_count++; np->flags = (np->flags & ~NF_DELSEL) | NF_SEL; } np->flags &= ~NF_DEL; } } else { NGDATA* np; for (np = first_ng; np; np = np->next) { if (np->flags & NF_DEL) { np->flags &= ~NF_DEL; catch_up(np, 0, 0); } } } break; case SM_OPTIONS: break; /* should probably be expanded... */ case SM_UNIVERSAL: break; default: if (sel_rereading) { /* Turn selections into unread selected articles. Let ** count_subjects() fix the counts after we're through. */ register SUBJECT* sp; sel_last_ap = NULL; sel_last_sp = NULL; for (sp = first_subject; sp; sp = sp->next) unkill_subject(sp); } else { if (sel_mode == SM_ARTICLE) article_walk(mark_DEL_as_READ, 0); else { register SUBJECT* sp; for (sp = first_subject; sp; sp = sp->next) { if (sp->flags & SF_DEL) { sp->flags &= ~SF_DEL; if (sel_mode == SM_THREAD) kill_thread(sp->thread, AFFECT_UNSEL); else kill_subject(sp, AFFECT_UNSEL); } } } } break; } } static bool mark_DEL_as_READ(ptr, arg) char* ptr; int arg; { register ARTICLE* ap = (ARTICLE*)ptr; if (ap->flags & AF_DEL) { ap->flags &= ~AF_DEL; set_read(ap); } return 0; } static int sel_command(ch) char_int ch; { int ret; if (can_home) goto_xy(0,sel_last_line); clean_screen = TRUE; term_scrolled = 0; page_line = 1; if (sel_mode == SM_NEWSGROUP) { if (sel_item_index < sel_page_item_cnt) set_ng(sel_items[sel_item_index].u.np); else ngptr = NULL; } do_command: *buf = ch; buf[1] = FINISHCMD; output_chase_phrase = TRUE; switch (ch) { case '>': sel_item_index = 0; if (next_page()) return DS_DISPLAY; break; case '<': sel_item_index = 0; if (prev_page()) return DS_DISPLAY; break; case '^': case Ctl('r'): sel_item_index = 0; if (first_page()) return DS_DISPLAY; break; case '$': sel_item_index = 0; if (last_page()) return DS_DISPLAY; break; case Ctl('l'): return DS_DISPLAY; case Ctl('^'): erase_line(0); /* erase the prompt */ removed_prompt |= 2; #ifdef MAILCALL setmail(TRUE); /* force a mail check */ #endif break; case '\r': case '\n': if (!selected_count && sel_page_item_cnt) { if (sel_rereading || sel_items[sel_item_index].sel != 2) select_item(sel_items[sel_item_index].u); } return DS_QUIT; case 'Z': case '\t': return DS_QUIT; case 'q': case 'Q': case '+': case '`': return DS_QUIT; case Ctl('Q'): case '\033': sel_ret = '\033'; return DS_QUIT; case '\b': case '\177': return DS_QUIT; case Ctl('k'): edit_kfile(); return DS_DISPLAY; case '&': case '!': erase_line(mousebar_cnt > 0); /* erase the prompt */ removed_prompt = 3; if (!finish_command(TRUE)) { /* get rest of command */ if (clean_screen) break; } else { PUSH_SELECTOR(); one_command = TRUE; perform(buf, FALSE); one_command = FALSE; if (term_line != sel_last_line+1 || term_scrolled) clean_screen = FALSE; POP_SELECTOR(); if (!save_sel_mode) return DS_RESTART; if (clean_screen) { erase_line(0); return DS_ASK; } } if ((ch = another_command(1)) != '\0') goto do_command; return DS_DISPLAY; case 'v': newline(); trn_version(); if ((ch = another_command(1)) != '\0') goto do_command; return DS_DISPLAY; case '\\': erase_line(mousebar_cnt > 0); /* erase the prompt */ removed_prompt = 3; if (sel_mode == SM_NEWSGROUP) printf("[%s] Cmd: ", ngptr? ngptr->rcline : "*End*"); else fputs("Cmd: ", stdout); fflush(stdout); getcmd(buf); if (*buf == '\\') goto the_default; if (*buf != ' ' && *buf != '\n' && *buf != '\r') { ch = *buf; goto do_command; } if (clean_screen) { erase_line(0); break; } if ((ch = another_command(1)) != '\0') goto do_command; return DS_DISPLAY; default: the_default: ret = extra_commands(ch); switch (ret) { case DS_ERROR: break; case DS_DOCOMMAND: ch = sel_ret; goto do_command; default: return ret; } strcpy(msg,"Type ? for help."); settle_down(); if (clean_screen) return DS_STATUS; printf("\n%s\n",msg); if ((ch = another_command(1)) != '\0') goto do_command; return DS_DISPLAY; } return DS_ASK; } static bool sel_perform_change(cnt, obj_type) long cnt; char* obj_type; { int ret; carriage_return(); if (page_line == 1) { disp_status_line = 1; if (term_line != sel_last_line+1 || term_scrolled) clean_screen = FALSE; } else clean_screen = FALSE; if (error_occurred) { print_lines(msg,NOMARKING); clean_screen = error_occurred = FALSE; } ret = perform_status_end(cnt, obj_type); if (ret) disp_status_line = 1; if (clean_screen) { if (ret != 2) { up_line(); return TRUE; } } else if (disp_status_line == 1) { print_lines(msg,NOMARKING); disp_status_line = 0; } init_pages(PRESERVE_PAGE); return FALSE; } #ifdef SCAN_ART #define SPECIAL_CMD_LETTERS "<+>^$!?&:/\\hDEJLNOPqQRSUXYZ\n\r\t\033;" #else #define SPECIAL_CMD_LETTERS "<+>^$!?&:/\\hDEJLNOPqQRSUXYZ\n\r\t\033" #endif static char another_command(ch) char_int ch; { bool skip_q = !ch; if (ch < 0) return 0; if (ch > 1) { read_tty(buf,1); ch = *buf; } else ch = pause_getcmd(); if (ch != 0 && ch != '\n' && ch != '\r' && (!skip_q || ch != 'q')) { if (ch > 0) { /* try to optimize the screen update for some commands. */ if (!index(sel_chars, ch) && (index(SPECIAL_CMD_LETTERS, ch) || ch == Ctl('k'))) { sel_ret = ch; return ch; } pushchar(ch | 0200); } } return '\0'; } static int article_commands(ch) char_int ch; { switch (ch) { case 'U': sel_cleanup(); sel_rereading = !sel_rereading; sel_page_sp = NULL; sel_page_app = NULL; if (!cache_range(sel_rereading? absfirst : firstart, lastart)) sel_rereading = !sel_rereading; return DS_RESTART; case '#': if (sel_page_item_cnt) { register SUBJECT* sp; for (sp = first_subject; sp; sp = sp->next) sp->flags &= ~SF_SEL; selected_count = 0; deselect_item(sel_items[sel_item_index].u); select_item(sel_items[sel_item_index].u); if (!keep_the_group_static) keep_the_group_static = 2; } return DS_QUIT; case 'N': case 'P': return DS_QUIT; case 'L': switch_dmode(&sel_art_dmode); /* sets msg */ return DS_DISPLAY; case 'Y': if (!dmcount) { strcpy(msg,"No marked articles to yank back."); return DS_STATUS; } yankback(); if (!sel_rereading) sel_cleanup(); disp_status_line = 1; count_subjects(CS_NORM); sel_page_sp = NULL; sel_page_app = NULL; init_pages(PRESERVE_PAGE); return DS_DISPLAY; case '=': if (!sel_rereading) sel_cleanup(); if (sel_mode == SM_ARTICLE) { set_selector(sel_threadmode, 0); sel_page_sp = sel_page_app? sel_page_app[0]->subj : NULL; } else { set_selector(SM_ARTICLE, 0); sel_page_app = 0; } count_subjects(CS_NORM); sel_item_index = 0; init_pages(FILL_LAST_PAGE); return DS_DISPLAY; case 'S': if (!sel_rereading) sel_cleanup(); erase_line(mousebar_cnt > 0); /* erase the prompt */ removed_prompt = 3; reask_output: in_char("Selector mode: Threads, Subjects, Articles?", 'o', "tsa"); #ifdef VERIFY printcmd(); #endif if (*buf == 'h' || *buf == 'H') { #ifdef VERBOSE IF(verbose) fputs("\n\ Type t or SP to display/select thread groups (threads the group, if needed).\n\ Type s to display/select subject groups.\n\ Type a to display/select individual articles.\n\ Type q to leave things as they are.\n\n\ ",stdout) FLUSH; ELSE #endif #ifdef TERSE fputs("\n\ t or SP selects thread groups (threads the group too).\n\ s selects subject groups.\n\ a selects individual articles.\n\ q does nothing.\n\n\ ",stdout) FLUSH; #endif clean_screen = FALSE; goto reask_output; } else if (*buf == 'q') { if (can_home) erase_line(0); return DS_ASK; } if (isupper(*buf)) *buf = tolower(*buf); set_sel_mode(*buf); count_subjects(CS_NORM); init_pages(FILL_LAST_PAGE); return DS_DISPLAY; case 'O': if (!sel_rereading) sel_cleanup(); erase_line(mousebar_cnt > 0); /* erase the prompt */ removed_prompt = 3; reask_sort: if (sel_mode == SM_ARTICLE) in_char( "Order by Date,Subject,Author,Number,subj-date Groups,Points?", 'q', "dsangpDSANGP"); else in_char("Order by Date, Subject, Count, Lines, or Points?", 'q', "dsclpDSCLP"); #ifdef VERIFY printcmd(); #endif if (*buf == 'h' || *buf == 'H') { #ifdef VERBOSE IF(verbose) { fputs("\n\ Type d or SP to order the displayed items by date.\n\ Type s to order the items by subject.\n\ Type p to order the items by score points.\n\ ",stdout) FLUSH; if (sel_mode == SM_ARTICLE) fputs("\ Type a to order the items by author.\n\ Type n to order the items by number.\n\ Type g to order the items in subject-groups by date.\n\ ",stdout) FLUSH; else fputs("\ Type c to order the items by count.\n\ ",stdout) FLUSH; fputs("\ Typing your selection in upper case it will reverse the selected order.\n\ Type q to leave things as they are.\n\n\ ",stdout) FLUSH; } ELSE #endif #ifdef TERSE { fputs("\n\ d or SP sorts by date.\n\ s sorts by subject.\n\ p sorts by points.\n\ ",stdout) FLUSH; if (sel_mode == SM_ARTICLE) fputs("\ a sorts by author.\n\ g sorts in subject-groups by date.\n\ ",stdout) FLUSH; else fputs("\ c sorts by count.\n\ ",stdout) FLUSH; fputs("\ Upper case reverses the sort.\n\ q does nothing.\n\n\ ",stdout) FLUSH; } #endif clean_screen = FALSE; goto reask_sort; } else if (*buf == 'q') { if (can_home) erase_line(0); return DS_ASK; } set_sel_sort(sel_mode,*buf); count_subjects(CS_NORM); sel_page_sp = NULL; sel_page_app = NULL; init_pages(FILL_LAST_PAGE); return DS_DISPLAY; case 'R': if (!sel_rereading) sel_cleanup(); set_selector(sel_mode, sel_sort * -sel_direction); count_subjects(CS_NORM); sel_page_sp = NULL; sel_page_app = NULL; init_pages(FILL_LAST_PAGE); return DS_DISPLAY; case 'E': if (!sel_rereading) sel_cleanup(); sel_exclusive = !sel_exclusive; count_subjects(CS_NORM); sel_page_sp = NULL; sel_page_app = NULL; init_pages(FILL_LAST_PAGE); sel_item_index = 0; return DS_DISPLAY; case 'X': case 'D': case 'J': if (!sel_rereading) { if (sel_mode == SM_ARTICLE) { register ARTICLE* ap; register ARTICLE** app; register ARTICLE** limit; limit = artptr_list + artptr_list_size; if (ch == 'D') app = sel_page_app; else app = artptr_list; while (app < limit) { ap = *app; if ((!(ap->flags & AF_SEL) ^ (ch == 'J')) || (ap->flags & AF_DEL)) if (ch == 'J' || !sel_exclusive || (ap->flags & AF_INCLUDED)) { set_read(ap); } app++; if (ch == 'D' && app == sel_next_app) break; } } else { register SUBJECT* sp; if (ch == 'D') sp = sel_page_sp; else sp = first_subject; while (sp) { if (((!(sp->flags & SF_SEL) ^ (ch == 'J')) && sp->misc) || (sp->flags & SF_DEL)) { if (ch == 'J' || !sel_exclusive || (sp->flags & SF_INCLUDED)) { kill_subject(sp, ch=='J'? AFFECT_ALL:AFFECT_UNSEL); } } sp = sp->next; if (ch == 'D' && sp == sel_next_sp) break; } } count_subjects(CS_UNSELECT); if (obj_count && (ch == 'J' || (ch == 'D' && !selected_count))) { init_pages(FILL_LAST_PAGE); sel_item_index = 0; return DS_DISPLAY; } if (artptr_list && obj_count) sort_articles(); } else if (ch == 'J') { register SUBJECT* sp; for (sp = first_subject; sp; sp = sp->next) deselect_subject(sp); selected_subj_cnt = selected_count = 0; return DS_DISPLAY; } return DS_QUIT; case 'T': if (!ThreadedGroup) { strcpy(msg,"Group is not threaded."); return DS_STATUS; } /* FALL THROUGH */ case 'A': if (!sel_page_item_cnt) { dingaling(); break; } erase_line(mousebar_cnt > 0); /* erase the prompt */ removed_prompt = 3; if (sel_mode == SM_ARTICLE) artp = sel_items[sel_item_index].u.ap; else { register SUBJECT* sp = sel_items[sel_item_index].u.sp; if (sel_mode == SM_THREAD) { while (!sp->misc) sp = sp->thread_link; } artp = sp->articles; } art = article_num(artp); /* This call executes the action too */ switch (ask_memorize(ch)) { case 'J': case 'j': case 'K': case ',': count_subjects(sel_rereading? CS_NORM : CS_UNSELECT); init_pages(PRESERVE_PAGE); strcpy(msg,"Kill memorized."); disp_status_line = 1; return DS_DISPLAY; case '+': case '.': case 'S': case 'm': strcpy(msg,"Selection memorized."); disp_status_line = 1; return DS_UPDATE; case 'c': case 'C': strcpy(msg,"Auto-commands cleared."); disp_status_line = 1; return DS_UPDATE; case 'q': return DS_UPDATE; case 'Q': break; } if (can_home) erase_line(0); break; #ifdef SCAN_ART case ';': sel_ret = ';'; return DS_QUIT; #endif case ':': if (sel_page_item_cnt) { if (sel_mode == SM_ARTICLE) artp = sel_items[sel_item_index].u.ap; else { register SUBJECT* sp = sel_items[sel_item_index].u.sp; if (sel_mode == SM_THREAD) { while (!sp->misc) sp = sp->thread_link; } artp = sp->articles; } art = article_num(artp); } else art = 0; /* FALL THROUGH */ case '/': erase_line(mousebar_cnt > 0); /* erase the prompt */ removed_prompt = 3; if (!finish_command(TRUE)) { /* get rest of command */ if (clean_screen) break; } else { if (ch == ':') { thread_perform(); if (!sel_rereading) { register SUBJECT* sp; for (sp = first_subject; sp; sp = sp->next) { if (sp->flags & SF_DEL) { sp->flags = 0; if (sel_mode == SM_THREAD) kill_thread(sp->thread, AFFECT_UNSEL); else kill_subject(sp, AFFECT_UNSEL); } } } } else { /* Force the search to begin at absfirst or firstart, ** depending upon whether they specified the 'r' option. */ art = lastart+1; switch (art_search(buf, sizeof buf, FALSE)) { case SRCH_ERROR: case SRCH_ABORT: break; case SRCH_INTR: errormsg("Interrupted"); break; case SRCH_DONE: case SRCH_SUBJDONE: case SRCH_FOUND: break; case SRCH_NOTFOUND: errormsg("Not found."); break; } } sel_item_index = 0; /* Recount, in case something has changed. */ count_subjects(sel_rereading? CS_NORM : CS_UNSELECT); if (sel_perform_change(ngptr->toread, "article")) return DS_UPDATE; if (clean_screen) return DS_DISPLAY; } if (another_command(1)) return DS_DOCOMMAND; return DS_DISPLAY; case 'c': erase_line(mousebar_cnt > 0); /* erase the prompt */ removed_prompt = 3; if ((ch = ask_catchup()) == 'y' || ch == 'u') { count_subjects(CS_UNSELECT); if (ch != 'u' && obj_count) { sel_page_sp = NULL; sel_page_app = NULL; init_pages(FILL_LAST_PAGE); return DS_DISPLAY; } sel_ret = 'Z'; return DS_QUIT; } if (ch != 'N') return DS_DISPLAY; if (can_home) erase_line(0); break; case 'h': case '?': univ_help(UHELP_ARTSEL); return DS_RESTART; case 'H': newline(); if (another_command(help_artsel())) return DS_DOCOMMAND; return DS_DISPLAY; default: return DS_ERROR; } return DS_ASK; } static int newsgroup_commands(ch) char_int ch; { switch (ch) { case Ctl('n'): case Ctl('p'): return DS_QUIT; case 'U': sel_cleanup(); sel_rereading = !sel_rereading; sel_page_np = NULL; return DS_RESTART; case 'L': switch_dmode(&sel_grp_dmode); /* sets msg */ if (*sel_grp_dmode != 's' && !multirc->first->datasrc->desc_sf.hp) { newline(); return DS_RESTART; } return DS_DISPLAY; case 'X': case 'D': case 'J': if (!sel_rereading) { register NGDATA* np; if (ch == 'D') np = sel_page_np; else np = first_ng; while (np) { if (((!(np->flags&NF_SEL) ^ (ch=='J')) && np->toread!=TR_UNSUB) || (np->flags & NF_DEL)) { if (ch == 'J' || (np->flags & NF_INCLUDED)) catch_up(np, 0, 0); np->flags &= ~(NF_DEL|NF_SEL); } np = np->next; if (ch == 'D' && np == sel_next_np) break; } if (ch == 'J' || (ch == 'D' && !selected_count)) { init_pages(FILL_LAST_PAGE); if (sel_total_obj_cnt) { sel_item_index = 0; return DS_DISPLAY; } } } else if (ch == 'J') { register NGDATA* np; for (np = first_ng; np; np = np->next) np->flags &= ~NF_DELSEL; selected_count = 0; return DS_DISPLAY; } return DS_QUIT; case '=': { NGDATA* np; sel_cleanup(); missing_count = 0; for (np = first_ng; np; np = np->next) { if (np->toread > TR_UNSUB && np->toread < ng_min_toread) newsgroup_toread++; np->abs1st = 0; } erase_line(0); #ifdef SUPPORT_NNTP check_active_refetch(TRUE); #endif return DS_RESTART; } case 'O': if (!sel_rereading) sel_cleanup(); erase_line(mousebar_cnt > 0); /* erase the prompt */ removed_prompt = 3; reask_sort: in_char("Order by Newsrc, Group name, or Count?", 'q', "ngcNGC"); #ifdef VERIFY printcmd(); #endif switch (*buf) { case 'n': case 'N': break; case 'g': case 'G': *buf += 's' - 'g'; /* Group name == SS_STRING */ break; case 'c': case 'C': break; case 'h': case 'H': #ifdef VERBOSE IF(verbose) { fputs("\n\ Type n or SP to order the newsgroups in the .newsrc order.\n\ Type g to order the items by group name.\n\ Type c to order the items by count.\n\ ",stdout) FLUSH; fputs("\ Typing your selection in upper case it will reverse the selected order.\n\ Type q to leave things as they are.\n\n\ ",stdout) FLUSH; } ELSE #endif #ifdef TERSE { fputs("\n\ n or SP sorts by .newsrc.\n\ g sorts by group name.\n\ c sorts by count.\n\ ",stdout) FLUSH; fputs("\ Upper case reverses the sort.\n\ q does nothing.\n\n\ ",stdout) FLUSH; } #endif clean_screen = FALSE; goto reask_sort; default: if (can_home) erase_line(0); return DS_ASK; } set_sel_sort(sel_mode,*buf); sel_page_np = NULL; init_pages(FILL_LAST_PAGE); return DS_DISPLAY; case 'R': if (!sel_rereading) sel_cleanup(); set_selector(sel_mode, sel_sort * -sel_direction); sel_page_np = NULL; init_pages(FILL_LAST_PAGE); return DS_DISPLAY; case 'E': if (!sel_rereading) sel_cleanup(); sel_exclusive = !sel_exclusive; sel_page_np = NULL; init_pages(FILL_LAST_PAGE); sel_item_index = 0; return DS_DISPLAY; case ':': #if 0 if (ngptr != current_ng) { recent_ng = current_ng; current_ng = ngptr; } /* FALL THROUGH */ #endif case '/': erase_line(mousebar_cnt > 0); /* erase the prompt */ removed_prompt = 3; if (!finish_command(TRUE)) { /* get rest of command */ if (clean_screen) break; } else { if (ch == ':') { ngsel_perform(); } else { #ifdef NGSEARCH ngptr = NULL; switch (ng_search(buf,FALSE)) { case NGS_ERROR: case NGS_ABORT: break; case NGS_INTR: errormsg("Interrupted"); break; case NGS_FOUND: case NGS_NOTFOUND: case NGS_DONE: break; } ngptr = current_ng; #else notincl("/"); #endif } sel_item_index = 0; if (sel_perform_change(newsgroup_toread, "newsgroup")) return DS_UPDATE; if (clean_screen) return DS_DISPLAY; } if (another_command(1)) return DS_DOCOMMAND; return DS_DISPLAY; case 'c': if (sel_item_index < sel_page_item_cnt) set_ng(sel_items[sel_item_index].u.np); else { strcpy(msg, "No newsgroup to catchup."); disp_status_line = 1; return DS_UPDATE; } if (ngptr != current_ng) { recent_ng = current_ng; current_ng = ngptr; } erase_line(mousebar_cnt > 0); /* erase the prompt */ removed_prompt = 3; if ((ch = ask_catchup()) == 'y' || ch == 'u') return DS_DISPLAY; if (ch != 'N') return DS_DISPLAY; if (can_home) erase_line(0); break; case 'h': case '?': univ_help(UHELP_NGSEL); return DS_RESTART; case 'H': newline(); if (another_command(help_ngsel())) return DS_DOCOMMAND; return DS_DISPLAY; #ifdef SCAN_ART case ';': sel_ret = ';'; return DS_QUIT; #endif default: { SEL_UNION u; int ret; bool was_at_top = !sel_prior_obj_cnt; PUSH_SELECTOR(); if (!(removed_prompt & 2)) { erase_line(mousebar_cnt > 0); /* erase the prompt */ removed_prompt = 3; printf("[%s] Cmd: ", ngptr? ngptr->rcline : "*End*"); fflush(stdout); } dfltcmd = "\\"; set_mode('r','n'); if (ch == '\\') { putchar(ch); fflush(stdout); } else pushchar(ch | 0200); do { ret = input_newsgroup(); } while (ret == ING_INPUT); set_mode('s','w'); POP_SELECTOR(); switch (ret) { #ifdef SUPPORT_NNTP case ING_NOSERVER: if (multirc) { if (!was_at_top) (void) first_page(); return DS_RESTART; } /* FALL THROUGH */ #endif case ING_QUIT: sel_ret = 'q'; return DS_QUIT; case ING_ERROR: return DS_ERROR; case ING_ERASE: if (clean_screen) { erase_line(0); return DS_ASK; } break; default: if (!save_sel_mode) return DS_RESTART; if (term_line == sel_last_line) newline(); if (term_line != sel_last_line+1 || term_scrolled) clean_screen = FALSE; break; } sel_item_index = 0; init_pages(PRESERVE_PAGE); if (ret == ING_SPECIAL && ngptr && ngptr->toread < ng_min_toread){ ngptr->flags |= NF_INCLUDED; sel_total_obj_cnt++; ret = ING_DISPLAY; } u.np = ngptr; if ((calc_page(u) || ret == ING_DISPLAY) && clean_screen) return DS_DISPLAY; if (ret == ING_MESSAGE) { clean_screen = 0; return DS_STATUS; } if (was_at_top) (void) first_page(); if (clean_screen) return DS_ASK; newline(); if (another_command(1)) return DS_DOCOMMAND; return DS_DISPLAY; } } return DS_ASK; } static int addgroup_commands(ch) char_int ch; { switch (ch) { case 'O': if (!sel_rereading) sel_cleanup(); erase_line(mousebar_cnt > 0); /* erase the prompt */ removed_prompt = 3; reask_sort: in_char("Order by Natural-order, Group name, or Count?", 'q', "ngcNGC"); #ifdef VERIFY printcmd(); #endif switch (*buf) { case 'n': case 'N': break; case 'g': case 'G': *buf += 's' - 'g'; /* Group name == SS_STRING */ break; case 'c': case 'C': break; case 'h': case 'H': #ifdef VERBOSE IF(verbose) { fputs("\n\ Type n or SP to order the items in their naturally occurring order.\n\ Type g to order the items by newsgroup name.\n\ Type c to order the items by article count.\n\ ",stdout) FLUSH; fputs("\ Typing your selection in upper case it will reverse the selected order.\n\ Type q to leave things as they are.\n\n\ ",stdout) FLUSH; } ELSE #endif #ifdef TERSE { fputs("\n\ n or SP sorts by natural order.\n\ g sorts by newsgroup name.\n\ c sorts by article count.\n\ ",stdout) FLUSH; fputs("\ Upper case reverses the sort.\n\ q does nothing.\n\n\ ",stdout) FLUSH; } #endif clean_screen = FALSE; goto reask_sort; default: if (can_home) erase_line(0); return DS_ASK; } set_sel_sort(sel_mode,*buf); sel_page_np = NULL; init_pages(FILL_LAST_PAGE); return DS_DISPLAY; case 'U': sel_cleanup(); sel_rereading = !sel_rereading; sel_page_gp = NULL; return DS_RESTART; case 'R': if (!sel_rereading) sel_cleanup(); set_selector(sel_mode, sel_sort * -sel_direction); sel_page_gp = NULL; init_pages(FILL_LAST_PAGE); return DS_DISPLAY; case 'E': if (!sel_rereading) sel_cleanup(); sel_exclusive = !sel_exclusive; sel_page_gp = NULL; init_pages(FILL_LAST_PAGE); return DS_DISPLAY; case 'L': switch_dmode(&sel_grp_dmode); /* sets msg */ if (*sel_grp_dmode != 's' && !datasrc->desc_sf.hp) { newline(); return DS_RESTART; } return DS_DISPLAY; case 'X': case 'D': case 'J': if (!sel_rereading) { register ADDGROUP* gp; if (ch == 'D') gp = sel_page_gp; else gp = first_addgroup; while (gp) { if ((!(gp->flags&AGF_SEL) ^ (ch=='J')) || (gp->flags & AGF_DEL)) { if (ch == 'J' || (gp->flags & AGF_INCLUDED)) gp->flags |= AGF_EXCLUDED; gp->flags &= ~(AGF_DEL|AGF_SEL); } gp = gp->next; if (ch == 'D' && gp == sel_next_gp) break; } if (ch == 'J' || (ch == 'D' && !selected_count)) { init_pages(FILL_LAST_PAGE); if (sel_total_obj_cnt) { sel_item_index = 0; return DS_DISPLAY; } } } else if (ch == 'J') { register ADDGROUP* gp; for (gp = first_addgroup; gp; gp = gp->next) gp->flags &= ~AGF_DELSEL; selected_count = 0; return DS_DISPLAY; } return DS_QUIT; case ':': case '/': erase_line(mousebar_cnt > 0); /* erase the prompt */ removed_prompt = 3; if (!finish_command(TRUE)) { /* get rest of command */ if (clean_screen) break; } else { if (ch == ':') { addgrp_sel_perform(); } else { #ifdef NGSEARCH switch (ng_search(buf,FALSE)) { case NGS_ERROR: case NGS_ABORT: break; case NGS_INTR: errormsg("Interrupted"); break; case NGS_FOUND: case NGS_NOTFOUND: case NGS_DONE: break; } #else notincl("/"); #endif } sel_item_index = 0; if (sel_perform_change(newsgroup_toread, "newsgroup")) return DS_UPDATE; if (clean_screen) return DS_DISPLAY; } if (another_command(1)) return DS_DOCOMMAND; return DS_DISPLAY; case 'h': case '?': univ_help(UHELP_ADDSEL); return DS_RESTART; case 'H': newline(); if (another_command(help_addsel())) return DS_DOCOMMAND; return DS_DISPLAY; default: return DS_ERROR; } return DS_ASK; } static int multirc_commands(ch) char_int ch; { switch (ch) { case 'R': set_selector(sel_mode, sel_sort * -sel_direction); sel_page_mp = NULL; init_pages(FILL_LAST_PAGE); return DS_DISPLAY; case 'E': if (!sel_rereading) sel_cleanup(); sel_exclusive = !sel_exclusive; sel_page_mp = NULL; init_pages(FILL_LAST_PAGE); return DS_DISPLAY; case '/': /*$$$$*/ break; case 'h': case '?': univ_help(UHELP_MULTIRC); return DS_RESTART; case 'H': newline(); if (another_command(help_multirc())) return DS_DOCOMMAND; return DS_DISPLAY; default: return DS_ERROR; } return DS_ASK; } static int option_commands(ch) char_int ch; { switch (ch) { case 'R': set_selector(sel_mode, sel_sort * -sel_direction); sel_page_op = 1; init_pages(FILL_LAST_PAGE); return DS_DISPLAY; case 'E': if (!sel_rereading) sel_cleanup(); sel_exclusive = !sel_exclusive; sel_page_op = 1; init_pages(FILL_LAST_PAGE); return DS_DISPLAY; case 'S': return DS_QUIT; case '/': { extern COMPEX optcompex; SEL_UNION u; char* s; char* pattern; int i, j; erase_line(mousebar_cnt > 0); /* erase the prompt */ removed_prompt = 3; if (!finish_command(TRUE)) /* get rest of command */ break; s = cpytill(buf,buf+1,'/'); for (pattern = buf; *pattern == ' '; pattern++) ; if ((s = compile(&optcompex,pattern,TRUE,TRUE)) != NULL) { strcpy(msg,s); return DS_STATUS; } i = j = sel_items[sel_item_index].u.op; do { if (++i > obj_count) i = 1; if (*options_ini[i].item == '*') continue; if (execute(&optcompex,options_ini[i].item)) break; } while (i != j); u.op = i; if (!(option_flags[i] & OF_INCLUDED)) { for (j = i-1; *options_ini[j].item != '*'; j--) ; option_flags[j] |= OF_SEL; init_pages(FILL_LAST_PAGE); calc_page(u); return DS_DISPLAY; } if (calc_page(u)) return DS_DISPLAY; return DS_ASK; } case 'h': case '?': univ_help(UHELP_OPTIONS); return DS_RESTART; case 'H': newline(); if (another_command(help_options())) return DS_DOCOMMAND; return DS_DISPLAY; default: return DS_ERROR; } return DS_ASK; } static int universal_commands(ch) char_int ch; { switch (ch) { case 'R': set_selector(sel_mode, sel_sort * -sel_direction); sel_page_univ = NULL; init_pages(FILL_LAST_PAGE); return DS_DISPLAY; case 'E': if (!sel_rereading) sel_cleanup(); sel_exclusive = !sel_exclusive; sel_page_univ = NULL; init_pages(FILL_LAST_PAGE); return DS_DISPLAY; #ifdef SCAN_ART case ';': sel_ret = ';'; return DS_QUIT; #endif case 'U': sel_cleanup(); sel_rereading = !sel_rereading; sel_page_univ = NULL; return DS_RESTART; case Ctl('e'): univ_edit(); univ_redofile(); sel_cleanup(); sel_page_univ = NULL; return DS_RESTART; case 'h': case '?': univ_help(UHELP_UNIV); return DS_RESTART; case 'H': newline(); if (another_command(help_univ())) return DS_DOCOMMAND; return DS_DISPLAY; #ifdef SCORE case 'O': if (!sel_rereading) sel_cleanup(); erase_line(mousebar_cnt > 0); /* erase the prompt */ removed_prompt = 3; reask_sort: in_char("Order by Natural, or score Points?", 'q', "npNP"); #ifdef VERIFY printcmd(); #endif if (*buf == 'h' || *buf == 'H') { #ifdef VERBOSE IF(verbose) { fputs("\n\ Type n or SP to order the items in the natural order.\n\ Type p to order the items by score points.\n\ ",stdout) FLUSH; fputs("\ Typing your selection in upper case it will reverse the selected order.\n\ Type q to leave things as they are.\n\n\ ",stdout) FLUSH; } ELSE #endif #ifdef TERSE { fputs("\n\ n or SP sorts by natural order.\n\ p sorts by score.\n\ ",stdout) FLUSH; fputs("\ Upper case reverses the sort.\n\ q does nothing.\n\n\ ",stdout) FLUSH; } #endif clean_screen = FALSE; goto reask_sort; } else if (*buf == 'q' || (tolower(*buf) != 'n' && tolower(*buf) != 'p')) { if (can_home) erase_line(0); return DS_ASK; } set_sel_sort(sel_mode,*buf); sel_page_univ = NULL; init_pages(FILL_LAST_PAGE); return DS_DISPLAY; #endif case '~': univ_virt_pass(); sel_cleanup(); sel_page_univ = NULL; return DS_RESTART; default: break; } return DS_ERROR; } static void switch_dmode(dmode_cpp) char** dmode_cpp; { char* s; if (!*++*dmode_cpp) { do { --*dmode_cpp; } while ((*dmode_cpp)[-1] != '*'); } switch (**dmode_cpp) { case 's': s = "short"; break; case 'm': s = "medium"; break; case 'd': s = "date"; break; case 'l': s = "long"; break; } sprintf(msg,"(%s display style)",s); disp_status_line = 1; } static int find_line(y) int y; { int i; for (i = 0; i < sel_page_item_cnt; i++) { if (sel_items[i].line > y) break; } if (i > 0) i--; return i; } /* On click: * btn = 0 (left), 1 (middle), or 2 (right) + 4 if double-clicked; * x = 0 to COLS-1; y = 0 to LINES-1; * btn_clk = 0, 1, or 2 (no 4); x_clk = x; y_clk = y. * On release: * btn = 3; x = release's x; y = release's y; * btn_clk = click's 0, 1, or 2; x_clk = click's x; y_clk = click's y. */ void selector_mouse(btn, x,y, btn_clk, x_clk,y_clk) int btn; int x, y; int btn_clk; int x_clk, y_clk; { if (check_mousebar(btn, x,y, btn_clk, x_clk,y_clk)) return; if (btn != 3) { /* Handle button-down event */ switch (btn_clk) { case 0: case 1: if (y > 0 && y < sel_last_line) { if (btn & 4) { pushchar(btn_clk == 0? '\n' : 'Z'); mouse_is_down = FALSE; } else { force_sel_pos = find_line(y); if (UseSelNum) { pushchar(('0' + (force_sel_pos+1) % 10) | 0200); pushchar(('0' + (force_sel_pos+1)/10) | 0200); } else { pushchar(sel_chars[force_sel_pos] | 0200); } if (btn == 1) pushchar(Ctl('g') | 0200); } } break; case 2: break; } } else { /* Handle the button-up event */ switch (btn_clk) { case 0: if (!y) pushchar('<' | 0200); else if (y >= sel_last_line) pushchar(' '); else { int i = find_line(y); if (sel_items[i].line != term_line) { if (UseSelNum) { pushchar(('0' + (i+1) % 10) | 0200); pushchar(('0' + (i+1) / 10) | 0200); } else { pushchar(sel_chars[i] | 0200); } pushchar('-' | 0200); force_sel_pos = i; } } break; case 1: if (!y) pushchar('<' | 0200); else if (y >= sel_last_line) pushchar('>' | 0200); break; case 2: /* move forward or backwards a page: * if cursor in top half: backwards * if cursor in bottom half: forwards */ if (y < LINES/2) pushchar('<' | 0200); else pushchar('>' | 0200); break; } } } /* Icky placement, but the PUSH/POP stuff is local to this file */ int univ_visit_group(gname) char* gname; { PUSH_SELECTOR(); univ_visit_group_main(gname); POP_SELECTOR(); return 0; /* later may have some error return values */ } /* later consider returning universal_selector() value */ void univ_visit_help(where) int where; { PUSH_SELECTOR(); PUSH_UNIV_SELECTOR(); univ_help_main(where); (void)universal_selector(); POP_UNIV_SELECTOR(); POP_SELECTOR(); }