1 /*
2  *	(c) Copyright 1990, Kim Fabricius Storm.  All rights reserved.
3  *      Copyright (c) 1996-2005 Michael T Pins.  All rights reserved.
4  *
5  * 	selection mode menu
6  */
7 
8 #include <string.h>
9 #include <ctype.h>
10 #include "config.h"
11 #include "global.h"
12 #include "articles.h"
13 #include "answer.h"
14 #include "db.h"
15 #include "execute.h"
16 #include "folder.h"
17 #include "group.h"
18 #include "init.h"
19 #include "keymap.h"
20 #include "kill.h"
21 #include "macro.h"
22 #include "match.h"
23 #include "menu.h"
24 #include "more.h"
25 #include "newsrc.h"
26 #include "regexp.h"
27 #include "save.h"
28 #include "sort.h"
29 #include "nn_term.h"
30 
31 /* menu.c */
32 
33 struct menu_info;
34 
35 static article_number root_article(register article_number root);
36 static article_number next_root_article(register article_number root);
37 static void     set_root_if_closed(void);
38 static article_number thread_counters(article_number art);
39 static void     cursor_at_id(void);
40 static attr_type closed_attr(register struct menu_info * mi, char *cbuf);
41 static void     mark(void);
42 static void     toggle(void);
43 static int      do_auto_kill(void);
44 static int      do_auto_select(regexp * re, int mode);
45 static int      quit_preview(int cmd);
46 static void     count_selected_articles(void);
47 static int      show_articles(void);
48 static int      get_k_cmd_1(void);
49 static int      get_k_cmd(void);
50 static int      repl_attr(register article_number first, register article_number last, register attr_type old, register attr_type new, int update);
51 static int      repl_attr_subject(attr_type old, attr_type new, int update);
52 static int      repl_attr_all(attr_type old, attr_type new, int update);
53 static void     get_purpose(char *purpose);
54 static int      do_consolidation(void);
55 
56 int             echo_prefix_key = 1;	/* echo prefix keys */
57 int             preview_window = 0;	/* size of preview window */
58 int             fmt_linenum = 1;/* menu line format */
59 int             fmt_rptsubj = 0;/* repeat identical subjects if !0 */
60 int             novice = 1;	/* novice mode -- use extended prompts */
61 int             long_menu = 0;	/* don't put empty lines around menu lines */
62 int             delay_redraw = 0;	/* prompt again if :-command clears
63 					 * screen */
64 int             slow_mode = 0;	/* mark selected articles with *s */
65 int             re_layout = 0;	/* Re: format presentation on menus */
66 int             collapse_subject = 25;	/* collapse long subjects at position */
67 int             conf_group_entry = 0;	/* ask whether group should be
68 					 * entered */
69 int             conf_entry_limit = 0;	/* ask only if more than .. unread */
70 int             mark_read_skip = 4;	/* effect of X command */
71 int             mark_read_return = 0;	/* effect of Z command */
72 int             mark_next_group = 0;	/* effect of N command */
73 int             show_purpose_mode = 1;	/* 0: never, 1: new, 2: always */
74 int             read_ret_next_page = 0;	/* Z returns to next page */
75 
76 int             consolidated_menu = 0;	/* show only root articles */
77 int             save_closed_mode = 13;	/* ask how to save closed subj, dflt
78 					 * all */
79 int             auto_select_rw = 0;	/* select subject read or written */
80 int             auto_select_closed = 1;	/* select all in closed subject */
81 int             menu_spacing = 0;	/* number of screen lines per menu
82 					 * line */
83 char           *counter_delim_left = "[";
84 char           *counter_delim_right = "] ";
85 int             counter_padding = 5;	/* counters are padded to align
86 					 * subjects */
87 
88 int             auto_preview_mode = 0;	/* preview rather than select */
89 int             preview_continuation = 12;	/* what to do after preview */
90 int             preview_mark_read = 1;	/* previewed articles are A_READ */
91 int             select_on_sender = 0;	/* find command selects on sender */
92 int             auto_select_subject = 0;	/* auto select articles with
93 						 * same subj. */
94 int             auto_read_limit = 0;	/* ignore auto_read_mode if less
95 					 * articles */
96 
97 char            delayed_msg[100] = "";	/* give to msg() after redraw */
98 
99 int             flush_typeahead = 0;
100 
101 extern int      also_read_articles;
102 extern int      merged_menu;
103 extern int      case_fold_search;
104 extern int      ignore_fancy_select;
105 extern int      kill_file_loaded;
106 extern int      new_read_prompt;
107 extern int      any_message;
108 extern int      enable_stop;
109 extern int      alt_cmd_key, in_menu_mode;
110 extern int      mouse_y;	/* mouse event position */
111 extern int      mouse_state;
112 extern key_type erase_key;
113 extern int      get_from_macro;
114 
115 static regexp  *regular_expr = NULL;
116 
117 static int      firstl;		/* first menu line */
118 
119 static article_number firsta;	/* first article on menu (0 based) */
120 static article_number nexta;	/* first article on next menu */
121 static article_number cura;		/* current article */
122 static article_number next_cura;	/* article to become cura if >= 0 */
123 static article_number numa;		/* no of articles on menu - 1 */
124 static attr_type last_attr;
125 
126 #define INTERVAL1	('z' - 'a' + 1)
127 #define INTERVAL2	('9' - '0' + 1)
128 
129 static char     ident[] = "abcdefghijklmnopqrstuvwxyz0123456789";
130 
131 char            attributes[30] = " .,+=#! **";	/* Corresponds to A_XXXX in
132 						 * data.h */
133 
134 static int      menu_length;	/* current no of line on menu */
135 static int      menu_articles;	/* current no of articles on menu */
136 
137 static struct menu_info {	/* info for each menu line */
138     int             mi_cura;	/* cura corresponding to this menu line */
139     int             mi_total;	/* total number of articles with this subject */
140     int             mi_unread;	/* no of unread articles with this subject */
141     int             mi_selected;/* no of selected articles with this subject */
142     int             mi_left;	/* no of articles marked for later viewing */
143     int             mi_art_id;	/* article id (for mark()) */
144 }               menu_info[INTERVAL1 + INTERVAL2];
145 
146 static struct menu_info *art_id_to_mi[INTERVAL1 + INTERVAL2];
147 
148 #define IS_VISIBLE(ah)	(((ah)->flag & (A_CLOSED | A_ROOT_ART)) != A_CLOSED)
149 
150 int
prt_replies(int level)151 prt_replies(int level)
152 {
153     int             re;
154 
155     if (level == 0)
156 	return 0;
157     re = level & 0x80;
158     level &= 0x7f;
159 
160     switch (re_layout) {
161 	case 1:
162 	    if (!re)
163 		return 0;
164 	    so_printf(">");
165 	    return 1;
166 	case 2:
167 	    switch (level) {
168 		case 0:
169 		    return 0;
170 		case 1:
171 		    so_printf(">");
172 		    return 1;
173 		default:
174 		    so_printf("%d>", level);
175 		    return level < 10 ? 2 : 3;
176 	    }
177 	case 3:
178 	    so_printf("Re: ");
179 	    return 4;
180 	case 4:
181 	    if (level == 0 && re)
182 		level++;
183 	    break;
184     }
185 
186     if (level < 10) {
187 	so_printf("%-.*s", level, ">>>>>>>>>");
188 	return level;
189     }
190     so_printf(">>>%3d >>>>", level);
191     return 11;
192 }
193 
194 static          article_number
root_article(register article_number root)195 root_article(register article_number root)
196 {
197     register article_header *ah = articles[root];
198 
199     if (ah->flag & A_ROOT_ART)
200 	return root;
201 
202     while (root > 0) {
203 	if (articles[root]->flag & A_ROOT_ART)
204 	    break;
205 	root--;
206     }
207     return root;
208 }
209 
210 static          article_number
next_root_article(register article_number root)211 next_root_article(register article_number root)
212 {
213     while (++root < n_articles)
214 	if (articles[root]->flag & A_ROOT_ART)
215 	    break;
216     return root;
217 }
218 
219 static void
set_root_if_closed(void)220 set_root_if_closed(void)
221 {
222     if (articles[firsta + cura]->flag & A_CLOSED)
223 	cura = root_article(firsta + cura) - firsta;
224 }
225 
226 /*
227  *	count info for thread containing article #art (must be on menu!)
228  *	returns article number for next root article
229  */
230 
231 static          article_number
thread_counters(article_number art)232 thread_counters(article_number art)
233 {
234     register struct menu_info *mi;
235     register article_number n;
236     int             total, unread, selected, left;
237 
238     if (!(articles[art]->flag & A_CLOSED))
239 	return art + 1;
240 
241     total = unread = selected = left = 0;
242     n = art = root_article(art);
243 
244     while (n < n_articles) {
245 	if (articles[n]->attr == 0)
246 	    unread++;
247 	else if (articles[n]->attr & A_SELECT)
248 	    selected++;
249 	else if (articles[n]->attr == A_LEAVE_NEXT)
250 	    left++;
251 	total++;
252 	if (++n == n_articles)
253 	    break;
254 	if (articles[n]->flag & A_ROOT_ART)
255 	    break;
256     }
257     unread += selected;
258 
259     mi = menu_info + articles[art]->menu_line;
260     mi->mi_total = total;
261     mi->mi_unread = unread;
262     mi->mi_selected = selected;
263     mi->mi_left = left;
264     return n;
265 }
266 
267 static void
cursor_at_id(void)268 cursor_at_id(void)
269 {
270     gotoxy(0, firstl + articles[firsta + cura]->menu_line);
271     fl;				/* place cursor at current article id */
272     save_xy();
273 }
274 
275 static          attr_type
closed_attr(register struct menu_info * mi,char * cbuf)276 closed_attr(register struct menu_info * mi, char *cbuf)
277 {
278     char            lft[10], sel[10], unr[10];
279     attr_type       cattr;
280 
281     if (mi->mi_total == mi->mi_left)
282 	cattr = A_LEAVE_NEXT;
283     else if (mi->mi_unread == 0)
284 	cattr = A_READ;
285     else if (mi->mi_total == mi->mi_selected)
286 	cattr = A_SELECT;
287     else if (auto_select_closed == 1 && mi->mi_unread == mi->mi_selected)
288 	cattr = A_SELECT;
289     else if (mi->mi_selected)
290 	cattr = A_KILL;		/* pseudo flag -> highlight cbuf */
291     else
292 	cattr = 0;
293 
294     lft[0] = sel[0] = unr[0] = NUL;
295     if (mi->mi_left && mi->mi_left < mi->mi_unread)
296 	sprintf(lft, "%d,", mi->mi_left);
297     if (mi->mi_selected && mi->mi_selected < mi->mi_unread)
298 	sprintf(sel, "%d/", mi->mi_selected);
299     if (mi->mi_unread && mi->mi_unread < mi->mi_total)
300 	sprintf(unr, "%d:", mi->mi_unread);
301     sprintf(cbuf, "%s%s%s%d", lft, sel, unr, mi->mi_total);
302 
303     return cattr;
304 }
305 
306 static int      subj_indent;
307 
308 static void
mark(void)309 mark(void)
310 {
311     register article_header *ah;
312     register struct menu_info *mi;
313     int             lno, lnum, lsubj, lname;
314     int             pad;
315     char            cbuf[80];
316     attr_type       cattr = 0;
317 
318     ah = articles[firsta + cura];
319     if (!IS_VISIBLE(ah))
320 	return;
321 
322     last_attr = ah->attr;
323     if (ah->disp_attr == A_NOT_DISPLAYED) {
324 	mi = &menu_info[ah->menu_line];
325 	lno = firstl + ah->menu_line;
326 	gotoxy(0, lno);
327 	tputc(ident[mi->mi_art_id]);
328 	cattr = closed_attr(mi, cbuf);
329 	goto print_line;
330     }
331     if (cura < 0 || cura > numa)
332 	return;
333 
334     lno = firstl + ah->menu_line;
335 
336     if (ah->flag & A_CLOSED) {
337 	struct menu_info old;
338 	char            oldctr[80];
339 
340 	mi = &menu_info[ah->menu_line];
341 	old = *mi;
342 	thread_counters(firsta + cura);
343 	if (old.mi_total == mi->mi_total &&
344 	    old.mi_selected == mi->mi_selected &&
345 	    old.mi_left == mi->mi_left &&
346 	    old.mi_unread == mi->mi_unread)
347 	    return;
348 
349 	cattr = closed_attr(mi, cbuf);
350 
351 	if (!slow_mode)
352 	    goto print_line;
353 	closed_attr(&old, oldctr);
354 	if (strcmp(cbuf, oldctr))
355 	    goto print_line;
356 	last_attr = cattr;
357     }
358     if (last_attr == ah->disp_attr)
359 	return;
360 
361     /* A_AUTO_SELECT will not occur here! */
362 
363     if (!slow_mode) {
364 	if (last_attr == A_SELECT) {
365 	    if ((ah->disp_attr & A_SELECT) == 0) {
366 		goto print_line;
367 	    }
368 	} else {
369 	    if (ah->disp_attr & A_SELECT) {
370 		goto print_line;
371 	    }
372 	}
373     }
374     gotoxy(1, lno);
375     tputc(attributes[ah->attr]);
376     goto out;
377 
378 print_line:
379 
380 /* menu line formats:						*/
381 /*		1  3    8 10     20 22        xx		*/
382 /*		:  :    :  :      :  :         :		*/
383 /*	-1	id name:8 subject				*/
384 /*	0	id name           subject      +lines		*/
385 /*	1	id name       lines  subject			*/
386 /*	2	id  lines  subject				*/
387 /*	3	id subject					*/
388 /*	4	id   subject  (or as 1 if short subject)	*/
389 
390     if ((ah->flag & A_CLOSED) == 0) {
391 	cattr = ah->attr;
392 	cbuf[0] = NUL;
393     }
394     if (fmt_linenum > 4)
395 	fmt_linenum = 1;
396 
397     if (!slow_mode && (cattr & A_SELECT)) {
398 	if (so_gotoxy(1, lno, 1) == 0)
399 	    tputc(attributes[A_SELECT]);
400     } else {
401 	gotoxy(1, lno);
402 	tputc(cattr == A_KILL ? ' ' : attributes[cattr]);
403     }
404 
405     if (ah->lines < 10)
406 	lnum = 1;
407     else if (ah->lines < 100)
408 	lnum = 2;
409     else if (ah->lines < 1000)
410 	lnum = 3;
411     else if (ah->lines < 10000)
412 	lnum = 4;
413     else
414 	lnum = 5;
415 
416     lsubj = Columns - cookie_size - 2;	/* ident char + space */
417 
418     switch (fmt_linenum) {
419 
420 	case -1:
421 	    lsubj -= 9;
422 	    so_printf("%-8.8s ", ah->sender);
423 	    goto no_counters;
424 
425 	case 0:
426 	    lsubj -= Name_Length + 1 + 2 + lnum;	/* name. .subj. +.lines */
427 	    so_printf("%-*s ", Name_Length, ah->sender);
428 	    break;
429 
430 	case 4:
431 	    if ((int) ah->subj_length > (lsubj - Name_Length - 5))
432 		if (fmt_rptsubj || lno == firstl || (ah->flag & A_SAME) == 0) {
433 		    so_printf("  ");
434 		    lsubj -= 2;
435 		    break;
436 		}
437 	    /* else use layout 1, so fall thru */
438 
439 	    /* FALLTHROUGH */
440 	case 1:
441 	    lsubj -= Name_Length + 5;
442 	    /* name.lines.  .subj (name may be shortened) */
443 	    lname = Name_Length + 2 - lnum;
444 	    so_printf("%-*.*s ", lname, lname, ah->sender);
445 	    so_printf(ah->lines >= 0 ? "%d  " : "?  ", ah->lines);
446 	    break;
447 
448 	case 2:
449 	    lsubj -= 6;
450 	    so_printf(ah->lines >= 0 ? "%5d " : "    ? ", ah->lines);
451 	    break;
452 
453 	case 3:
454 	    break;
455     }
456 
457     if (cbuf[0]) {
458 	so_printf("%s", counter_delim_left);
459 	if (cattr == A_KILL)
460 	    so_gotoxy(-1, -1, 0);
461 	so_printf("%s", cbuf);
462 	if (cattr == A_KILL)
463 	    so_end();
464 	so_printf("%s", counter_delim_right);
465 	pad = strlen(cbuf) + strlen(counter_delim_left) + strlen(counter_delim_right);
466 	if (cattr == A_KILL)
467 	    pad += cookie_size * 2;
468 	lsubj -= pad > subj_indent ? pad : subj_indent;
469 	pad = subj_indent - pad;
470     } else {
471 	lsubj -= subj_indent;
472 	pad = subj_indent;
473     }
474     if (pad > 0) {
475 	if (pad > 20)
476 	    pad = 20;
477 	so_printf("                    " + 20 - pad);
478     }
479 no_counters:
480     if (!fmt_rptsubj && lno > firstl && ah->flag & A_SAME) {
481 	if (ah->replies == 0 || prt_replies(ah->replies) == 0)
482 	    so_printf("-");
483     } else {
484 	lsubj -= prt_replies(ah->replies);
485 	if (lsubj >= (int) ah->subj_length)
486 	    so_printf("%s", ah->subject);
487 	else if (collapse_subject < 0)
488 	    so_printf("%-.*s", lsubj, ah->subject);
489 	else {
490 	    if (collapse_subject > 0)
491 		so_printf("%-.*s", collapse_subject, ah->subject);
492 	    so_printf("<>%s", ah->subject + ah->subj_length - lsubj + collapse_subject + 2);
493 	}
494     }
495 
496     if (fmt_linenum == 0)
497 	so_printf(ah->lines >= 0 ? " +%d" : " +?", ah->lines);
498 
499     so_end();
500     if ((ah->flag & A_CLOSED) && lsubj > (int) ah->subj_length)
501 	clrline_noflush();
502 
503 out:
504     ah->disp_attr = last_attr;
505     return;
506 }
507 
508 static void
toggle(void)509 toggle(void)
510 {
511     last_attr = articles[firsta + cura]->attr =
512     articles[firsta + cura]->attr & A_SELECT ? 0 : A_SELECT;
513 }
514 
515 static int
do_auto_kill(void)516 do_auto_kill(void)
517 {
518     register article_number i;
519     register article_header *ah, **ahp;
520     int             any = 0;
521 
522     for (i = 0, ahp = articles; i < n_articles; i++, ahp++) {
523 	ah = *ahp;
524 	if (auto_select_article(ah, 0)) {
525 	    ah->attr = A_KILL;
526 	    any = 1;
527 	}
528     }
529     return any;
530 }
531 
532 /*
533  *	perform auto selections that are not already selected
534  *	if article is in range firsta..firsta+numa (incl) mark article
535  */
536 
537 static int
do_auto_select(regexp * re,int mode)538 do_auto_select(regexp * re, int mode)
539 {
540     register article_number i, o_cura;
541     register article_header *ah, **ahp;
542     int             count = 0, should_mark;
543 
544     if (mode == 1 && re == NULL)
545 	if (!kill_file_loaded && !init_kill())
546 	    return 0;
547 
548     o_cura = cura;
549     should_mark = 0;
550 
551     /*
552      * note: this code assumes that a visible article will be found before
553      * anything is marked
554      */
555     for (i = 0, ahp = articles; i < n_articles; i++, ahp++) {
556 	ah = *ahp;
557 	if (IS_VISIBLE(ah)) {
558 	    if (should_mark) {
559 		mark();
560 		should_mark = 0;
561 	    }
562 	    cura = i - firsta;
563 	}
564 	if (re != NULL) {
565 	    if (!regexec_cf(re, select_on_sender ? ah->sender : ah->subject))
566 		continue;
567 	} else if (!auto_select_article(ah, mode))
568 	    continue;
569 
570 	count++;
571 	if (ah->attr & A_SELECT)
572 	    continue;
573 	ah->attr = A_SELECT;
574 	if (firsta <= i && i <= (firsta + numa))
575 	    should_mark = 1;
576     }
577 
578     if (should_mark)
579 	mark();
580 
581     if (count)
582 	msg("Selected %d article%s", count, plural((long) count));
583     else
584 	msg("No selections");
585 
586     cura = o_cura;
587     return 0;
588 }
589 
590 static int
quit_preview(int cmd)591 quit_preview(int cmd)
592 {
593     int             op;
594 
595     if ((firsta + cura) >= n_articles)
596 	return 1;
597     op = preview_continuation;
598     if (cmd == MC_PREVIEW_NEXT)
599 	op /= 10;
600     op %= 10;
601     switch (op) {
602 	case 0:
603 	    return 1;
604 	case 1:
605 	    return 0;
606 	case 2:
607 	    return articles[firsta + cura]->flag & A_ROOT_ART;
608     }
609     return 0;
610 }
611 
612 long            n_selected;
613 int             show_art_next_invalid;
614 
615 static void
count_selected_articles(void)616 count_selected_articles(void)
617 {
618     register article_number cur;
619 
620     n_selected = 0;
621     for (cur = 0; cur < n_articles; cur++) {
622 	if (articles[cur]->attr & A_SELECT)
623 	    n_selected++;
624     }
625 }
626 
627 static int
show_articles(void)628 show_articles(void)
629 {
630     register article_number cur, next, prev = -1;
631     register article_header *ah;
632     article_number  elim_list[1];
633     register int    mode;
634     int             cmd, again;
635     attr_type       o_attr;
636 
637     do {
638 	for (cur = 0; cur < n_articles; cur++) {
639 	    if (articles[cur]->attr & A_SELECT)
640 		break;
641 	}
642 
643 	while (cur < n_articles) {
644 
645 	    for (next = cur + 1; next < n_articles; next++) {
646 		if (articles[next]->attr & A_SELECT)
647 		    break;
648 	    }
649 	    show_art_next_invalid = 0;
650 
651     show:
652 	    ah = articles[cur];
653 	    o_attr = ah->attr;
654 	    ah->attr = 0;
655 
656 	    if (auto_select_rw && !ignore_fancy_select &&
657 		!auto_select_article(ah, 1))
658 		enter_kill_file(current_group, ah->subject, 6, 30);
659 
660 	    if (new_read_prompt)
661 		count_selected_articles();
662 
663 	    mode = 0;
664 	    if (prev >= 0)
665 		mode |= MM_PREVIOUS;
666 	    if (next == n_articles)
667 		mode |= MM_LAST_SELECTED;
668 	    if ((cur + 1) >= n_articles)
669 		mode |= MM_LAST_ARTICLE;
670 	    if (cur == 0)
671 		mode |= MM_FIRST_ARTICLE;
672 
673 	    cmd = more(ah, mode, 0);
674 
675 	    switch (cmd) {
676 
677 		case MC_DO_KILL:
678 		    if (do_auto_kill()) {
679 			elim_list[0] = cur;
680 			elim_articles(elim_list, 1);
681 			cur = elim_list[0];
682 			if (cur >= n_articles || cur < 0)
683 			    cur = n_articles;
684 			else {
685 			    for (prev = cur; prev >= 0; --prev)
686 				if ((articles[prev]->attr & A_READ) != 0)
687 				    break;
688 			    for (; cur < n_articles; ++cur)
689 				if ((articles[cur]->attr & A_SELECT) != 0)
690 				    break;
691 			}
692 			continue;
693 		    }
694 		    break;
695 
696 		case MC_DO_SELECT:
697 		    break;
698 
699 		case MC_PREV:
700 		    ah->attr = o_attr;
701 		    if (prev == next)
702 			break;
703 
704 		    next = cur;
705 		    cur = prev;
706 		    prev = next;
707 		    goto show;
708 
709 		case MC_NEXTSUBJ:
710 		    ah->attr = A_READ;
711 		    for (next = cur + 1; next < n_articles; next++) {
712 			if ((ah = articles[next])->flag & A_ROOT_ART)
713 			    break;
714 			ah->attr = A_READ;
715 		    }
716 		    for (; next < n_articles; next++) {
717 			if (articles[next]->attr & A_SELECT)
718 			    break;
719 		    }
720 		    break;
721 
722 		case MC_ALLSUBJ:
723 		    ah->attr = A_READ;
724 		    for (next = cur + 1; next < n_articles; next++) {
725 			ah = articles[next];
726 			if (ah->flag & A_ROOT_ART)
727 			    break;
728 			ah->attr = A_SELECT;
729 		    }
730 		    for (next = cur + 1; next < n_articles; next++)
731 			if (articles[next]->attr & A_SELECT)
732 			    break;
733 		    break;
734 
735 		case MC_MENU:
736 		    ah->attr = o_attr;
737 		    if (nexta - firsta < n_articles) {
738 			/* Keep a little article context by making the */
739 			/* current article go on menu line 6 if possible */
740 			if (IS_VISIBLE(articles[cur]))
741 			    firsta = cur;
742 			else
743 			    firsta = root_article(cur);
744 			for (next = 0; firsta > 0 && next < 5; next++) {
745 			    firsta--;
746 			    if (!IS_VISIBLE(articles[firsta]))
747 				firsta = root_article(firsta);
748 			}
749 		    }
750 		    next_cura = cur - firsta;
751 
752 		    return MC_MENU;
753 
754 		case MC_NEXT:
755 		    if (ah->attr == 0)	/* Not set by more (sufficient ?!?!) */
756 			ah->attr = A_READ;
757 		    break;
758 
759 		case MC_BACK_ART:
760 		    ah->attr = o_attr ? o_attr : A_SEEN;
761 		    next = cur - 1;
762 		    break;
763 
764 		case MC_FORW_ART:
765 		    ah->attr = o_attr ? o_attr : A_SEEN;
766 		    next = cur + 1;
767 		    break;
768 
769 		case MC_NEXTGROUP:
770 		case MC_REENTER_GROUP:
771 		case MC_QUIT:
772 		    ah->attr = o_attr;
773 		    return cmd;
774 
775 		case MC_READGROUP:
776 		    for (cur = 0; cur < n_articles; cur++) {
777 			ah = articles[cur];
778 			if (ah->attr == 0 || (ah->attr & A_SELECT))
779 			    ah->attr = A_READ;
780 		    }
781 		    return MC_NEXTGROUP;
782 	    }
783 
784 	    if (show_art_next_invalid)
785 		for (next = cur + 1; next < n_articles; next++) {
786 		    if (articles[next]->attr & A_SELECT)
787 			break;
788 		}
789 	    prev = cur;
790 	    cur = next;
791 	}
792 
793 	for (cur = 0; cur < n_articles; cur++)
794 	    if (articles[cur]->attr & A_SELECT)
795 		break;
796 
797 	again = 0;
798 	if (cur < n_articles)
799 	    continue;
800 	for (cur = 0; cur < n_articles; cur++) {
801 	    ah = articles[cur];
802 	    if (ah->attr == A_LEAVE) {
803 		if (again == 0) {
804 		    prompt("Show left over articles again now? ");
805 		    if (yes(0) <= 0)
806 			break;
807 		}
808 		ah->attr = A_SELECT;
809 		again++;
810 	    }
811 	}
812 
813 	if (again > 1)
814 	    sprintf(delayed_msg, "Showing %d articles again", again);
815     } while (again);
816 
817     return MC_READGROUP;
818 }
819 
820 static article_number      article_id;
821 static key_type cur_key;
822 static int      is_k_select;	/* set when K_ARTICLE_ID was really K_SELECT */
823 
824 #define MOUSE   (mouse_y >= 0)
825 
826 static int
mouse_map(int map)827 mouse_map(int map)
828 {
829     /* remap mouse functions back into normal functions */
830     if (map == K_M_SELECT) {
831 	map = K_ARTICLE_ID;
832     } else if (map == K_M_PREVIEW) {
833 	map = K_PREVIEW;
834     } else if (map == K_M_SELECT_SUBJECT) {
835 	map = K_SELECT_SUBJECT;
836     } else if (map == K_M_SELECT_RANGE) {
837 	map = K_SELECT_RANGE;
838     } else
839 	return map;
840 
841     /* deal with mouse article selection */
842     if (MOUSE) {
843 	if ((mouse_y >= firstl) && (mouse_y <= firstl + menu_length - 1)) {
844 	    article_id = mouse_y - firstl;
845 	    article_id = art_id_to_mi[article_id]->mi_cura;
846 	}
847     }
848     return map;
849 }
850 
851 static int
get_k_cmd_1(void)852 get_k_cmd_1(void)
853 {
854     register int    c, map;
855     int            *key_map = menu_key_map;
856 
857     if (flush_typeahead)
858 	flush_input();
859 
860 loop:
861 
862     article_id = -1;
863 
864     if ((c = get_c()) & GETC_COMMAND) {
865 	cur_key = K_interrupt;
866 	map = c & ~GETC_COMMAND;
867     } else {
868 	cur_key = c;
869 	map = key_map[c];
870     }
871     if (s_hangup)
872 	map = K_QUIT;
873 
874     if (map & K_PREFIX_KEY) {
875 	key_map = keymaps[map & ~K_PREFIX_KEY].km_map;
876 	if (echo_prefix_key)
877 	    msg("%s", key_name(cur_key));
878 	goto loop;
879     }
880     is_k_select = 0;
881     if (map == K_SELECT) {
882 	map = K_ARTICLE_ID;
883 	article_id = cura;
884 	is_k_select = 1;
885     } else if (map & K_ARTICLE_ID) {
886 	article_id = map & ~K_ARTICLE_ID;
887 	map = K_ARTICLE_ID;
888 
889 	if (article_id < 0 || article_id >= menu_articles) {
890 	    ding();
891 	    goto loop;
892 	}
893 	article_id = art_id_to_mi[article_id]->mi_cura;
894     } else
895 	map = mouse_map(map);
896 
897     if (any_message && map != K_LAST_MESSAGE)
898 	clrmsg(-1);
899     return map;
900 }
901 
902 static int
get_k_cmd(void)903 get_k_cmd(void)
904 {
905     register int    map;
906 
907     map = get_k_cmd_1();
908     if (map & K_MACRO)
909 	map = orig_menu_map[cur_key];
910     return map;
911 }
912 
913 
914 char           *
pct(long start,long end,long first,long last)915 pct(long start, long end, long first, long last)
916 {
917     long            n = end - start;
918     static char     buf[16];
919     char           *fmt;
920 
921     if (first <= start || n <= 0) {
922 	if (last >= end || n <= 0)
923 	    return "All";
924 	else
925 	    fmt = "Top %d%%";
926     } else if (last >= end) {
927 	return "Bot";
928     } else {
929 	fmt = "%d%%";
930     }
931 
932     sprintf(buf, fmt, ((last - start) * 100) / n);
933     return buf;
934 }
935 
936 static int
repl_attr(register article_number first,register article_number last,register attr_type old,register attr_type new,int update)937 repl_attr(register article_number first, register article_number last, register attr_type old, register attr_type new, int update)
938 {
939     int             any;
940     register article_header *ah;
941     article_number  ocura = cura;
942 
943     if (new == old)
944 	return 0;
945     if (new == A_KILL)
946 	update = 0;
947 
948     any = 0;
949     cura = -1;
950     while (first < last) {
951 	ah = articles[first];
952 	if (cura >= 0 && ah->flag & A_ROOT_ART) {
953 	    set_root_if_closed();
954 	    mark();
955 	    cura = -1;
956 	}
957 	if (old == A_KILL || ah->attr == old) {
958 	    ah->attr = new;
959 	    if (update && first >= firsta && first < nexta) {
960 		cura = first - firsta;
961 		if ((ah->flag & A_CLOSED) == 0) {
962 		    mark();
963 		    cura = -1;
964 		}
965 	    }
966 	    any = 1;
967 	}
968 	first++;
969     }
970 
971     if (cura >= 0) {
972 	set_root_if_closed();
973 	mark();
974     }
975     cura = update ? last - firsta : ocura;
976     return any;
977 }
978 
979 static int
repl_attr_subject(attr_type old,attr_type new,int update)980 repl_attr_subject(attr_type old, attr_type new, int update)
981 {
982     int             f, l;
983 
984     f = root_article(firsta + cura);
985     l = next_root_article(firsta + cura);
986     if (old == A_SELECT)
987 	(void) repl_attr(f, l, A_AUTO_SELECT, A_SELECT, update);
988     return repl_attr(f, l, old, new, update);
989 }
990 
991 static int
repl_attr_all(attr_type old,attr_type new,int update)992 repl_attr_all(attr_type old, attr_type new, int update)
993 {
994     return repl_attr((article_number) 0, n_articles, old, new, update);
995 }
996 
997 static void
get_purpose(char * purpose)998 get_purpose(char *purpose)
999 {
1000 
1001 #ifdef	CACHE_PURPOSE
1002     register char  *cp;
1003 
1004     cp = purp_lookup(current_group->group_name);
1005     strncpy(purpose, cp, 76);
1006     return;
1007 
1008 #else
1009     FILE           *f;
1010     char            line[256], *group;
1011     register char  *cp, *pp;
1012     register int    len;
1013 
1014     if (current_group == NULL)
1015 	return;
1016     if ((current_group->master_flag & M_VALID) == 0)
1017 	return;
1018     if (current_group->group_flag & G_FAKED)
1019 	return;
1020 
1021     if ((f = open_purpose_file()) == NULL)
1022 	return;
1023 
1024     group = current_group->group_name;
1025     len = current_group->group_name_length;
1026 
1027     while (fgets(line, 256, f) != NULL) {
1028 	if (!isascii(line[len]) || !isspace(line[len]))
1029 	    continue;
1030 	if (strncmp(line, group, len))
1031 	    continue;
1032 	cp = line + len;
1033 	while (*cp && isspace(*cp))
1034 	    cp++;
1035 	for (pp = purpose, len = 76; --len >= 0 && *cp && *cp != NL;)
1036 	    *pp++ = *cp++;
1037 	*pp = NUL;
1038     }
1039 #endif				/* CACHE_PURPOSE */
1040 }
1041 
1042 /*
1043  * bypass_consolidation may be set to temporarily overrule the global
1044  * consolidated_menu variable:
1045  *	1:	don't consolidate	(e.g. for G=...  )
1046  *	2:	do consolidate
1047  *	3:	use consolidated_mode
1048  */
1049 
1050 int             bypass_consolidation = 0;
1051 static int      cur_bypass = 0;	/* current bypass status (see below) */
1052 
1053 static int
do_consolidation(void)1054 do_consolidation(void)
1055 {
1056     int             consolidate;
1057 
1058     switch (bypass_consolidation) {
1059 	case 0:
1060 	    break;
1061 	case 1:
1062 	    cur_bypass = -1;	/* no consolidation */
1063 	    break;
1064 	case 2:
1065 	    cur_bypass = 1;	/* force consolidation */
1066 	    break;
1067 	case 3:
1068 	    cur_bypass = 0;	/* reset bypass to use consolidated_menu */
1069 	    break;
1070     }
1071     bypass_consolidation = 0;
1072 
1073     if (cur_bypass)
1074 	consolidate = cur_bypass > 0;
1075     else
1076 	consolidate = consolidated_menu;
1077 
1078     if (consolidate)
1079 	for (nexta = 0; nexta < n_articles; nexta++)
1080 	    articles[nexta]->flag |= A_CLOSED;
1081     else
1082 	for (nexta = 0; nexta < n_articles; nexta++)
1083 	    articles[nexta]->flag &= ~A_CLOSED;
1084 
1085     return consolidate;
1086 }
1087 
1088 int
menu(fct_type print_header)1089 menu(fct_type print_header)
1090 {
1091     register int    k_cmd, cur_k_cmd;
1092     register article_header *ah;
1093     register struct menu_info *mi;
1094     int             consolidate, o_bypass;
1095     int             last_k_cmd;
1096     int             menu_cmd = 0, temp;
1097     int             save_selected;
1098     article_number  last_save;
1099     attr_type       orig_attr, junk_attr;
1100     int             doing_unshar, did_unshar, junk_prompt;
1101     char           *fname, *savemode;
1102     int             maxa;	/* max no of articles per menu page */
1103     article_number  o_firsta, temp1 = 0, temp2;
1104     int             o_mode;	/* for recursive calls */
1105     static int      menu_level = 0;
1106     char            purpose[80], pr_fmt[60];
1107     article_number  elim_list[3];
1108     int             entry_check;
1109     int             auto_read;
1110     long            o_selected;
1111 
1112 #define	menu_return(cmd) \
1113     { menu_cmd = (cmd); goto menu_exit; }
1114 
1115     flush_input();
1116 
1117     o_firsta = firsta;
1118     o_mode = in_menu_mode;
1119     o_selected = n_selected;
1120 
1121     in_menu_mode = 1;
1122 
1123     menu_level++;
1124 
1125     if (menu_level == 1) {
1126 	if ((current_group->group_flag & G_COUNTED)
1127 	    && n_articles != current_group->unread_count) {
1128 	    add_unread(current_group, -1);
1129 	    current_group->unread_count = n_articles;
1130 	    add_unread(current_group, 0);
1131 	}
1132 	entry_check = conf_group_entry && n_articles > conf_entry_limit;
1133 	auto_read = auto_read_limit < 0 || n_articles <= auto_read_limit;
1134     } else {
1135 	entry_check = 0;
1136 	auto_read = 0;
1137     }
1138 
1139     sprintf(pr_fmt,
1140 	    menu_level == 1 ?
1141 	    "\1\2-- SELECT %s-----%%s-----\1" :
1142 	    "\1\2-- SELECT %s-----%%s-----<%s%d>--\1",
1143 	    novice ? "-- help:? " : "",
1144 	    novice ? "level " : "",
1145 	    menu_level);
1146 
1147     purpose[0] = NUL;
1148     if (!merged_menu)
1149 	switch (show_purpose_mode) {
1150 	    case 0:
1151 		break;
1152 	    case 1:
1153 		if ((current_group->group_flag & G_NEW) == 0)
1154 		    break;
1155 		/* FALLTHROUGH */
1156 	    case 2:
1157 		get_purpose(purpose);
1158 		if (purpose[0])
1159 		    strcpy(delayed_msg, purpose);
1160 	}
1161 
1162     o_bypass = cur_bypass;
1163     cur_bypass = 0;
1164 
1165     consolidate = do_consolidation();
1166 
1167     firsta = 0;
1168     while (firsta < n_articles && articles[firsta]->attr == A_SEEN)
1169 	firsta++;
1170 
1171     if (firsta == n_articles)
1172 	firsta = 0;
1173 
1174     next_cura = -1;
1175     cur_k_cmd = K_UNBOUND;
1176 
1177 #ifdef HAVE_JOBCONTROL
1178 #define	REDRAW_CHECK	if (s_redraw) goto do_redraw
1179 
1180 do_redraw:
1181     /* safe to clear here, because we are going to redraw anyway */
1182     s_redraw = 0;
1183 #else
1184 #define REDRAW_CHECK
1185 #endif
1186 
1187 redraw:
1188     s_keyboard = 0;
1189 
1190 empty_menu_hack:		/* do: "s_keyboard=1; goto empty_menu_hack;" */
1191     if (!slow_mode)
1192 	s_keyboard = 0;
1193 
1194     nexta = firsta;
1195 
1196     clrdisp();
1197 
1198     if (entry_check) {
1199 	prompt_line = firstl = CALL(print_header) ();
1200 	prompt("\1Enter?\1 ");
1201 	if ((temp = yes(0)) <= 0) {
1202 	    if (temp < 0) {
1203 		prompt("\1Mark as read?\1 ");
1204 		if ((temp = yes(0)) < 0)
1205 		    menu_return(ME_QUIT);
1206 		if (temp > 0)
1207 		    repl_attr_all(A_KILL, A_READ, 0);
1208 	    }
1209 	    menu_return(ME_NEXT);
1210 	}
1211 	gotoxy(0, firstl);
1212 	clrline();
1213     }
1214     if (auto_read) {
1215 	auto_read = entry_check = 0;
1216 	if (repl_attr_all(0, A_AUTO_SELECT, 0)) {
1217 	    k_cmd = K_READ_GROUP_UPDATE;
1218 	    if (purpose[0])
1219 		strcpy(delayed_msg, purpose);
1220 	    else
1221 		sprintf(delayed_msg, "Entering %s, %ld articles",
1222 			current_group->group_name, (long) n_articles);
1223 	    goto do_auto_read;
1224 	}
1225     }
1226     if (!entry_check)
1227 	firstl = CALL(print_header) ();
1228     entry_check = 0;
1229 
1230 
1231     maxa = Lines - preview_window - firstl - 2;
1232     if (!long_menu)
1233 	firstl++, maxa -= 2;
1234 
1235     if (maxa > (INTERVAL1 + INTERVAL2))
1236 	maxa = INTERVAL1 + INTERVAL2;
1237 
1238 nextmenu:
1239 
1240     no_raw();
1241     gotoxy(0, firstl);
1242     clrpage();
1243 
1244     if (articles[nexta]->flag & A_CLOSED)
1245 	nexta = root_article(nexta);
1246     firsta = nexta;
1247     cura = 0;
1248 
1249     REDRAW_CHECK;
1250 
1251     menu_length = 0;
1252     menu_articles = 0;
1253     goto draw_menu;
1254 
1255 partial_redraw:
1256     next_cura = cura;
1257 partial_redraw_nc:
1258     nexta = firsta + cura;
1259     menu_length = articles[nexta]->menu_line;
1260     menu_articles = menu_info[menu_length].mi_art_id;
1261     gotoxy(0, firstl + menu_length);
1262     clrpage();
1263 
1264 draw_menu:
1265     subj_indent = consolidate ? counter_padding : 0;
1266 
1267     if (!s_keyboard) {
1268 	int             first_menu_line = menu_length;
1269 
1270 	mi = menu_info + menu_length;
1271 	while (nexta < n_articles) {
1272 	    REDRAW_CHECK;
1273 
1274 	    ah = articles[nexta];
1275 	    if (ah->flag & A_HIDE) {
1276 		ah->menu_line = menu_length;	/* just in case.... */
1277 		continue;
1278 	    }
1279 	    if (menu_length > first_menu_line) {
1280 		switch (menu_spacing) {
1281 		    case 0:
1282 			break;
1283 		    case 1:
1284 			if ((ah->flag & A_ROOT_ART) == 0)
1285 			    break;
1286 			/* XXX: bug? Another fall? */
1287 		    case 2:
1288 			menu_length++;
1289 			mi++;
1290 			break;
1291 		}
1292 		if (menu_length >= maxa)
1293 		    break;
1294 	    }
1295 	    ah->menu_line = menu_length;
1296 	    art_id_to_mi[menu_articles] = mi;
1297 	    mi->mi_art_id = menu_articles++;
1298 
1299 	    mi->mi_cura = cura = nexta - firsta;
1300 	    if (ah->flag & A_CLOSED) {	/* skip rest of thread */
1301 		nexta = thread_counters(nexta);
1302 		if (nexta == firsta + cura + 1)
1303 		    ah->flag &= ~A_CLOSED;
1304 		else {
1305 		    article_number  tmpa = firsta + cura;
1306 		    while (++tmpa < nexta)
1307 			articles[tmpa]->menu_line = menu_length;
1308 		}
1309 	    } else
1310 		nexta++;
1311 	    ah->disp_attr = A_NOT_DISPLAYED;
1312 	    mark();
1313 	    if (++menu_length >= maxa)
1314 		break;
1315 	    mi++;
1316 	}
1317     }
1318     if (menu_length > maxa)
1319 	menu_length = maxa;
1320 
1321     fl;
1322     s_keyboard = 0;
1323 
1324     prompt_line = firstl + menu_length;
1325     if (!long_menu || menu_length < maxa)
1326 	prompt_line++;
1327 
1328     numa = nexta - firsta - 1;
1329     if (numa < 0)
1330 	prompt_line++;
1331 
1332 /* Changed by Stefan Schwarz (stefans@bauv.unibw-muenchen.de Nov. 17, 1992 */
1333     if (firsta + next_cura >= nexta)
1334 	next_cura = -1;
1335 /* End of changes*/
1336 
1337     if (next_cura >= 0) {
1338 	cura = next_cura;
1339 	set_root_if_closed();
1340 	next_cura = -1;
1341     } else {
1342 	cura = 0;
1343 	for (article_id = firsta; cura < numa; article_id++, cura++) {
1344 	    if ((articles[article_id]->attr & A_SELECT) == 0)
1345 		break;		/* ??? */
1346 	    if (!IS_VISIBLE(articles[article_id]))
1347 		continue;
1348 	}
1349 	if (!IS_VISIBLE(articles[article_id]))
1350 	    cura = root_article(article_id) - firsta;
1351     }
1352 
1353 build_prompt:
1354 
1355     nn_raw();
1356 
1357 Prompt:
1358 
1359     prompt(pr_fmt,
1360     pct(0L, (long) (n_articles - 1), (long) firsta, (long) (firsta + numa)));
1361 
1362     if (delayed_msg[0] != NUL) {
1363 	msg(delayed_msg);
1364 	delayed_msg[0] = NUL;
1365     }
1366 same_prompt:
1367 
1368     if (cura < 0 || cura > numa)
1369 	cura = 0;
1370 
1371     if (!IS_VISIBLE(articles[firsta + cura])) {
1372 	cura = next_root_article(firsta + cura) - firsta;
1373 	if (cura > numa)
1374 	    cura = 0;
1375     }
1376     if (numa >= 0) {
1377 	cursor_at_id();
1378     }
1379     last_k_cmd = cur_k_cmd;
1380 
1381 get_next_k_cmd:
1382     k_cmd = get_k_cmd_1();
1383 
1384 alt_key:
1385     if (k_cmd & K_MACRO) {
1386 	m_invoke(k_cmd & ~K_MACRO);
1387 	goto get_next_k_cmd;
1388     }
1389 #define STATE(state)  {k_cmd = state; goto new_state;}
1390 
1391 /* mouse clicks on top or bottom prompt bars  */
1392 #define MOUSE_MENU					\
1393 	 if (MOUSE) {					\
1394 	     if (mouse_y > firstl + menu_length ) {     \
1395 		 STATE(K_CONTINUE);			\
1396 	     } else if (mouse_y == firstl + menu_length ) { \
1397 		 STATE(K_UNBOUND);			\
1398 	     } else if (mouse_y < firstl - 1) {		\
1399 		 if (firsta > 0) {			\
1400 		     STATE(K_PREV_PAGE);		\
1401 		} else {				\
1402 		     STATE(K_PREVIOUS);			\
1403 		}					\
1404 	     } else if (mouse_y == firstl - 1) {		\
1405 		 STATE(K_UNBOUND);			\
1406 	     }						\
1407 	}
1408 
1409 new_state:
1410     switch (cur_k_cmd = k_cmd) {
1411 
1412 	case K_UNBOUND:
1413 	    ding();
1414 	    flush_input();
1415 
1416 	    /* FALLTHROUGH */
1417 	case K_INVALID:
1418 	    goto same_prompt;
1419 
1420 	case K_REDRAW:
1421 	    next_cura = cura;
1422 	    goto redraw;
1423 
1424 	case K_LAST_MESSAGE:
1425 	    msg((char *) NULL);
1426 	    goto same_prompt;
1427 
1428 	case K_HELP:
1429 	    if (numa < 0)
1430 		goto nextmenu;	/* give specific help here */
1431 	    display_help("menu");
1432 	    goto redraw;
1433 
1434 	case K_SHELL:
1435 	    if (group_file_name)
1436 		*group_file_name = NUL;
1437 	    if (shell_escape())
1438 		goto redraw;
1439 	    goto Prompt;
1440 
1441 	case K_VERSION:
1442 	    prompt(P_VERSION);
1443 	    goto same_prompt;
1444 
1445 	case K_EXTENDED_CMD:
1446 	    temp = consolidated_menu;
1447 	    switch (alt_command()) {
1448 
1449 		case AC_UNCHANGED:
1450 		    goto same_prompt;
1451 
1452 		case AC_QUIT:
1453 		    menu_return(ME_QUIT);
1454 		    break;	/* for lint. we can't actually get here */
1455 
1456 		case AC_PROMPT:
1457 		    goto Prompt;
1458 
1459 		case AC_REORDER:
1460 		    firsta = 0;
1461 		    consolidate = do_consolidation();
1462 		    goto redraw;
1463 
1464 		case AC_REDRAW:
1465 		    if (temp != consolidated_menu)
1466 			consolidate = do_consolidation();
1467 		    goto redraw;
1468 
1469 		case AC_KEYCMD:
1470 		    k_cmd = alt_cmd_key;
1471 		    goto alt_key;
1472 
1473 		case AC_REENTER_GROUP:
1474 		    menu_return(ME_REENTER_GROUP);
1475 	    }
1476 	    /* XXX: bug? fall */
1477 
1478 	case K_QUIT:
1479 	    menu_return(ME_QUIT);
1480 	    break;		/* for lint. we can't actually get here */
1481 
1482 	case K_M_TOGGLE:
1483 	    if (mouse_state) {
1484 		xterm_mouse_off();
1485 		mouse_state = 0;
1486 	    } else {
1487 		xterm_mouse_on();
1488 		mouse_state = 1;
1489 	    }
1490 	    goto Prompt;
1491 
1492 	case K_CANCEL:
1493 	    savemode = "Cancel";
1494 	    fname = "";
1495 	    goto cancel1;
1496 
1497 	case K_SAVE_NO_HEADER:
1498 	case K_SAVE_SHORT_HEADER:
1499 	case K_SAVE_FULL_HEADER:
1500 	case K_SAVE_HEADER_ONLY:
1501 	case K_PRINT:
1502 	case K_UNSHAR:
1503 	case K_PATCH:
1504 	case K_UUDECODE:
1505 
1506 	    if (numa < 0)
1507 		goto nextmenu;
1508 
1509 	    fname = init_save(k_cmd, &savemode);
1510 	    if (fname == NULL)
1511 		goto Prompt;
1512 
1513     cancel1:
1514 	    enable_stop = 0;
1515 	    save_selected = 0;
1516 	    doing_unshar = k_cmd == K_UNSHAR || k_cmd == K_PATCH;
1517 	    did_unshar = 0;
1518 
1519 	    m_startinput();
1520 
1521 	    if (novice)
1522 		msg(" * selected articles on this page, + all selected articles");
1523 
1524 	    while (!save_selected && !did_unshar) {
1525 		prompt("\1%s\1 %.*s Article (* +): ",
1526 		       savemode, Columns - 25, fname);
1527 
1528 		k_cmd = get_k_cmd();
1529 
1530 		if (k_cmd == K_SELECT_SUBJECT) {
1531 		    save_selected = 1;
1532 		    cura = 0;
1533 		    article_id = firsta;
1534 		    last_save = firsta + numa;
1535 		} else if (k_cmd == K_AUTO_SELECT) {
1536 		    save_selected = 1;
1537 		    cura = -firsta;
1538 		    article_id = 0;
1539 		    last_save = n_articles - 1;
1540 		} else if (k_cmd == K_ARTICLE_ID) {
1541 		    cura = article_id;
1542 		    article_id += firsta;
1543 		    last_save = article_id;
1544 		    if (articles[article_id]->flag & A_CLOSED) {
1545 			int             n = save_closed_mode % 10;
1546 
1547 			if (article_id == n_articles - 1)
1548 			    goto save_it;
1549 			if (articles[article_id + 1]->flag & A_ROOT_ART)
1550 			    goto save_it;
1551 
1552 			if (save_closed_mode >= 10) {
1553 			    prompt("\1%s thread\1 (r)oot (s)elected (u)nread (b)oth (a)ll  (%c)",
1554 				   savemode, "rsuba"[n]);
1555 			    switch (get_c()) {
1556 				case 'r':
1557 				    n = 0;
1558 				    break;
1559 				case 's':
1560 				    n = 1;
1561 				    break;
1562 				case 'u':
1563 				    n = 2;
1564 				    break;
1565 				case 'b':
1566 				    n = 3;
1567 				    break;
1568 				case 'a':
1569 				    n = 4;
1570 				    break;
1571 				case SP:
1572 				case CR:
1573 				case NL:
1574 				    break;
1575 				default:
1576 				    ding();
1577 				    goto Prompt;
1578 			    }
1579 			}
1580 			switch (n) {
1581 			    case 0:	/* save root only */
1582 				break;
1583 			    case 1:	/* save all selected */
1584 				save_selected = 1;
1585 				break;
1586 			    case 2:	/* save all unread */
1587 				save_selected = 2;
1588 				break;
1589 			    case 3:	/* save all selected + unread */
1590 				save_selected = 3;
1591 				break;
1592 			    case 4:	/* save all articles */
1593 				break;
1594 			}
1595 			if (n)
1596 			    last_save = next_root_article(article_id) - 1;
1597 			save_selected |= 8;	/* closed subject */
1598 			temp1 = cura;
1599 		    }
1600 		} else
1601 		    break;
1602 
1603 	save_it:
1604 		for (; article_id <= last_save; article_id++, cura++) {
1605 		    ah = articles[article_id];
1606 		    switch (save_selected & 0x3) {
1607 			case 0:
1608 			    break;
1609 			case 3:
1610 			    if (ah->attr == 0)
1611 				break;
1612 			    /* XXX: check fall */
1613 			case 1:
1614 			    if ((ah->attr & A_SELECT) == 0)
1615 				continue;
1616 			    break;
1617 			case 2:
1618 			    if (ah->attr == 0)
1619 				break;
1620 			    continue;
1621 		    }
1622 
1623 		    if (cur_k_cmd == K_CANCEL) {
1624 			if (current_group->group_flag & G_FOLDER) {
1625 			    fcancel(ah);
1626 			} else
1627 			    switch (cancel(ah)) {
1628 				case -1:
1629 				    did_unshar = 1;
1630 				    continue;
1631 				case 0:
1632 				    ah->attr = A_CANCEL;
1633 				    break;
1634 				default:
1635 				    continue;
1636 			    }
1637 
1638 			if (!did_unshar)
1639 			    mark();
1640 
1641 			continue;
1642 		    }
1643 		    if (doing_unshar) {
1644 			did_unshar++;
1645 		    } else if (ah->subject != NULL)
1646 			prompt("Processing '%.50s'...", ah->subject);
1647 		    else if (cura >= 0 && cura <= numa)
1648 			prompt("Processing %c...", ident[cura]);
1649 		    else
1650 			prompt("Processing entry %d...", article_id);
1651 
1652 		    if (save(ah)) {
1653 			ah->attr = A_READ;
1654 			if (doing_unshar)
1655 			    continue;
1656 
1657 			if (cura >= 0 && cura <= numa)
1658 			    mark();
1659 		    }
1660 		}
1661 		if (save_selected & 8) {
1662 		    save_selected = 0;	/* select closed */
1663 		    cura = temp1;
1664 		    /* mark(); */
1665 		}
1666 	    }
1667 
1668 	    if (save_selected)
1669 		cura = 0;
1670 
1671 	    m_endinput();
1672 
1673 	    enable_stop = 1;
1674 	    if (cur_k_cmd != K_CANCEL)
1675 		end_save();
1676 
1677 	    if (did_unshar) {
1678 		tprintf("\r\n");
1679 		any_key(0);
1680 		goto redraw;
1681 	    }
1682 	    goto Prompt;
1683 
1684 	case K_FOLLOW_UP:
1685 	case K_REPLY:
1686 	    if (numa < 0)
1687 		goto nextmenu;
1688 
1689 	    prompt(k_cmd == K_REPLY ?
1690 		   "\1Reply to author\1 of article: " :
1691 		   "\1Follow Up\1 to article: ");
1692 
1693 	    if (get_k_cmd() == K_ARTICLE_ID)
1694 		if (answer(articles[firsta + article_id], k_cmd, -1))
1695 		    goto redraw;
1696 
1697 	    goto Prompt;
1698 
1699 	case K_POST:
1700 	    if (post_menu())
1701 		goto redraw;
1702 	    goto Prompt;
1703 
1704 	case K_MAIL_OR_FORWARD:
1705 	    if (numa < 0)
1706 		goto nextmenu;
1707 
1708 	    prompt("\1Article to be forwarded\1 (SP if none): ");
1709 
1710 	    if ((k_cmd = get_k_cmd()) == K_ARTICLE_ID) {
1711 		if (answer(articles[firsta + article_id], K_MAIL_OR_FORWARD, 1))
1712 		    goto redraw;
1713 	    } else if (k_cmd == K_CONTINUE)
1714 		if (answer((article_header *) NULL, K_MAIL_OR_FORWARD, 0))
1715 		    goto redraw;
1716 
1717 	    goto Prompt;
1718 /*
1719       case K_CANCEL:
1720 	 if (numa < 0) goto nextmenu;
1721 
1722 	 if (current_group->group_flag & G_FOLDER) {
1723 	     prompt("\1Cancel Folder\1 Article: ");
1724 	     if (get_k_cmd() == K_ARTICLE_ID) {
1725 		 cura = article_id;
1726 		 fcancel(articles[firsta+article_id]);
1727 		 mark();
1728 	     }
1729 	     goto Prompt;
1730 	 }
1731 
1732 	 prompt("\1Cancel\1 Article: ");
1733 
1734 	 if (get_k_cmd() == K_ARTICLE_ID)
1735 	     if (cancel(articles[firsta+article_id]) & 1) goto redraw;
1736 	 goto Prompt;
1737 */
1738 	case K_UNSUBSCRIBE:
1739 	    if (unsubscribe(current_group)) {
1740 		if (current_group->group_flag & G_UNSUBSCRIBED)
1741 		    menu_return(ME_NEXT);
1742 		home();
1743 		CALL(print_header) ();
1744 	    }
1745 	    goto Prompt;
1746 
1747 	case K_GROUP_OVERVIEW:
1748 	    group_overview(-1);
1749 	    goto redraw;
1750 
1751 	case K_KILL_HANDLING:
1752 	    switch (kill_menu((article_header *) NULL)) {
1753 		case 0:	/* select */
1754 		    do_auto_select((regexp *) NULL, 2);
1755 		    break;
1756 		case 1:	/* kill */
1757 		    if (!do_auto_kill())
1758 			break;
1759 		    goto junk_killed_articles;
1760 		default:
1761 		    break;
1762 	    }
1763 	    goto Prompt;
1764 
1765 	case K_CONTINUE:	/* goto next menu page or show the articles */
1766 	    repl_attr(firsta, nexta, 0, A_SEEN, 0);
1767 	    /* FALLTHROUGH */
1768 	case K_CONTINUE_NO_MARK:	/* but don't mark unselected articles */
1769 	    if (nexta < n_articles)
1770 		goto nextmenu;
1771 	    break;
1772 
1773 	case K_READ_GROUP_THEN_SAME:
1774 	    if ((temp = mark_read_return))
1775 		goto do_marked_by;
1776 	    break;
1777 
1778 	case K_NEXT_GROUP_NO_UPDATE:
1779 	    if ((temp = mark_next_group))
1780 		goto do_marked_by;
1781 	    menu_return(ME_NEXT);
1782 	    break;		/* for lint. we can't actually get here */
1783 
1784 	case K_READ_GROUP_UPDATE:
1785 	    if ((temp = mark_read_skip) == 0)
1786 		break;
1787     do_marked_by:
1788 	    temp1 = 0;
1789 	    temp2 = n_articles;
1790 	    switch (temp) {
1791 		case 1:
1792 		    temp1 = firsta;
1793 		    /* FALLTHROUGH */
1794 		case 3:
1795 		    temp2 = nexta;
1796 		    break;
1797 		case 2:
1798 		    temp2 = firsta - 1;
1799 		    break;
1800 		case 4:
1801 		    break;
1802 	    }
1803 	    repl_attr(temp1, temp2, 0, A_SEEN, 0);
1804 	    if (k_cmd != K_NEXT_GROUP_NO_UPDATE)
1805 		break;
1806 	    menu_return(ME_NEXT);
1807 	    break;		/* for lint. we can't actually get here */
1808 
1809 	case K_PREVIOUS:
1810 	    menu_return(ME_PREV);
1811 	    break;		/* for lint. we can't actually get here */
1812 
1813 	case K_ADVANCE_GROUP:
1814 	case K_BACK_GROUP:
1815 	    if (merged_menu) {
1816 		msg("No possible on merged menu");
1817 		goto same_prompt;
1818 	    }
1819 	    /* FALLTHROUGH */
1820 	case K_GOTO_GROUP:
1821 	    temp1 = n_articles;
1822 
1823 	    switch (goto_group(k_cmd, (article_header *) NULL, (flag_type) 0)) {
1824 
1825 		case ME_REDRAW:
1826 		    firsta = 0;
1827 		    if (temp1 != n_articles && consolidate)
1828 			consolidate = do_consolidation();
1829 		    goto redraw;
1830 
1831 		case ME_NO_ARTICLES:
1832 		    msg("No selections made.");
1833 
1834 		    /* FALLTHROUGH */
1835 		case ME_NO_REDRAW:
1836 		    goto Prompt;
1837 
1838 		case ME_QUIT:
1839 		    menu_return(ME_QUIT);
1840 		    break;	/* for lint. we can't actually get here */
1841 
1842 		case ME_PREV:
1843 		    goto redraw;
1844 
1845 		case ME_NEXT:
1846 		    s_keyboard = 1;
1847 		    goto empty_menu_hack;
1848 	    }
1849 
1850 	    /* XXX: Fall ? */
1851 	case K_OPEN_SUBJECT:
1852 	    if (numa < 0)
1853 		goto nextmenu;
1854 	    prompt("\1Open subject\1");
1855 
1856 	    k_cmd = get_k_cmd();
1857 	    if (k_cmd != K_ARTICLE_ID) {
1858 		if (k_cmd != cur_k_cmd)
1859 		    goto Prompt;
1860 		article_id = cura;
1861 	    }
1862     open_subject:
1863 	    ah = articles[firsta + article_id];
1864 	    if (!(ah->flag & A_CLOSED) ||
1865 		(ah->flag & (A_ROOT_ART | A_NEXT_SAME)) == A_ROOT_ART)
1866 		goto Prompt;
1867 
1868 	    cura = article_id = root_article(firsta + article_id) - firsta;
1869 	    while (cura + firsta < n_articles) {
1870 		ah = articles[firsta + cura];
1871 		if (cura > article_id && ah->flag & A_ROOT_ART)
1872 		    break;
1873 		ah->flag &= ~A_CLOSED;
1874 		cura++;
1875 	    }
1876 	    cura = article_id;
1877 	    goto partial_redraw;
1878 
1879 	case K_CLOSE_SUBJECT:
1880 	    if (numa < 0)
1881 		goto nextmenu;
1882 	    prompt("\1Close subject\1");
1883 
1884 	    k_cmd = get_k_cmd();
1885 	    if (k_cmd != K_ARTICLE_ID) {
1886 		if (k_cmd != cur_k_cmd)
1887 		    goto Prompt;
1888 		article_id = cura;
1889 	    }
1890 	    ah = articles[firsta + article_id];
1891 	    if ((ah->flag & A_CLOSED) ||
1892 		(ah->flag & (A_ROOT_ART | A_NEXT_SAME)) == A_ROOT_ART) {
1893 		cura = next_root_article(firsta + cura) - firsta;
1894 		goto Prompt;
1895 	    }
1896 	    cura = article_id = root_article(firsta + article_id) - firsta;
1897 	    while (cura + firsta < n_articles) {
1898 		ah = articles[firsta + cura];
1899 		if (cura > article_id && ah->flag & A_ROOT_ART)
1900 		    break;
1901 		ah->flag |= A_CLOSED;
1902 		cura++;
1903 	    }
1904 	    cura = article_id;
1905 	    next_cura = next_root_article(firsta + cura) - firsta;
1906 	    if (next_cura < 0 || next_cura > numa)
1907 		next_cura = 0;
1908 	    if (cura >= 0)
1909 		goto partial_redraw_nc;
1910 	    articles[cura + firsta]->menu_line = articles[firsta]->menu_line;
1911 	    firsta += cura;
1912 	    goto redraw;
1913 
1914 	case K_LEAVE_NEXT:
1915 	case K_JUNK_ARTICLES:
1916 	    junk_prompt = cur_k_cmd == K_JUNK_ARTICLES ? 1 : 5;
1917 
1918 	    for (;;) {
1919 		switch (junk_prompt) {
1920 		    case 1:
1921 			if (novice)
1922 			    msg("Use J repeatedly to select other functions");
1923 			prompt("\1Mark read\1 S)een U)nmarked A)ll *+)selected a-z . [LN]");
1924 			junk_attr = A_READ;
1925 			break;
1926 		    case 2:
1927 			prompt("\1Unmark\1 S)een R)ead a-z [*+LAN.J] ");
1928 			junk_attr = 0;
1929 			break;
1930 		    case 3:
1931 			prompt("\1Select\1 L)eft-over, N(leave-next) [USRa-z.J]");
1932 			junk_attr = A_SELECT;
1933 			break;
1934 		    case 4:
1935 			prompt("\1Kill\1 R)ead S)een [LANU*+a-z.J]");
1936 			junk_attr = A_KILL;
1937 			break;
1938 		    case 5:
1939 			prompt("\1Leave\1 a-z .,/ * + U)nmarked [LANRSJ]");
1940 			junk_attr = A_LEAVE_NEXT;
1941 			break;
1942 		    default:
1943 			junk_prompt = 1;
1944 			continue;
1945 		}
1946 
1947 	junk_another:
1948 		if (cura < 0 || cura > numa)
1949 		    cura = 0;
1950 		cursor_at_id();
1951 
1952 		switch (get_k_cmd()) {
1953 		    case K_JUNK_ARTICLES:
1954 			junk_prompt++;	/* can be 0 */
1955 			continue;
1956 
1957 		    case K_ARTICLE_ID:
1958 			cura = article_id;
1959 			if (junk_attr == A_KILL)
1960 			    junk_attr = A_READ;
1961 			if (auto_select_closed > 0 && articles[firsta + cura]->flag & A_CLOSED)
1962 			    repl_attr_subject(A_KILL, junk_attr, 1);
1963 			else {
1964 			    articles[firsta + cura]->attr = junk_attr;
1965 			    mark();
1966 			    cura++;
1967 			}
1968 			goto junk_another;
1969 
1970 		    case K_NEXT_LINE:
1971 			cura++;
1972 			goto junk_another;
1973 
1974 		    case K_PREV_LINE:
1975 			--cura;
1976 			goto junk_another;
1977 
1978 		    case K_SELECT_SUBJECT:
1979 			if (junk_attr == A_KILL)
1980 			    junk_attr = A_READ;
1981 			repl_attr(firsta, nexta, A_AUTO_SELECT, A_SELECT, 0);
1982 			repl_attr(firsta, nexta, A_SELECT, junk_attr, 1);
1983 			goto Prompt;
1984 
1985 		    case K_AUTO_SELECT:
1986 			repl_attr_all(A_AUTO_SELECT, A_SELECT, 0);
1987 			orig_attr = A_SELECT;
1988 			break;
1989 
1990 		    default:
1991 			switch (cur_key) {
1992 			    case 'S':
1993 				orig_attr = A_SEEN;
1994 				break;
1995 
1996 			    case 'U':
1997 				orig_attr = 0;
1998 				break;
1999 
2000 			    case 'L':
2001 				if (junk_attr == A_KILL)
2002 				    junk_attr = A_READ;
2003 				orig_attr = A_LEAVE;
2004 				break;
2005 
2006 			    case 'A':
2007 				orig_attr = A_KILL;
2008 				break;
2009 
2010 			    case 'N':
2011 				orig_attr = A_LEAVE_NEXT;
2012 				break;
2013 
2014 			    case 'R':	/* kill read articles */
2015 				orig_attr = A_READ;
2016 				break;
2017 
2018 			    default:
2019 				goto Prompt;
2020 			}
2021 			break;
2022 		}
2023 		break;
2024 	    }
2025 	    if (nexta - firsta < n_articles) {
2026 		prompt("On all menu pages? ");
2027 		switch (yes(1)) {
2028 		    case -1:
2029 			goto Prompt;
2030 		    case 0:
2031 			if (!repl_attr(firsta, nexta, orig_attr, junk_attr, 1))
2032 			    goto Prompt;
2033 			break;
2034 		    case 1:
2035 			if (!repl_attr_all(orig_attr, junk_attr, 1))
2036 			    goto Prompt;
2037 			break;
2038 		}
2039 	    } else if (!repl_attr(firsta, nexta, orig_attr, junk_attr, 1))
2040 		goto Prompt;
2041 
2042 	    if (junk_attr != A_KILL)
2043 		goto Prompt;
2044 
2045     junk_killed_articles:
2046 	    elim_list[0] = firsta;
2047 	    elim_list[1] = firsta + cura;
2048 	    elim_list[2] = nexta;
2049 	    if (elim_articles(elim_list, 3)) {
2050 		firsta = elim_list[0];
2051 		goto redraw;
2052 	    }
2053 	    firsta = elim_list[0];
2054 	    cura = elim_list[1] - firsta;
2055 	    nexta = elim_list[2];
2056 	    goto Prompt;
2057 
2058 	case K_ARTICLE_ID:
2059 	    if (numa < 0)
2060 		goto nextmenu;
2061 
2062 	    MOUSE_MENU;
2063 
2064 	    if (!is_k_select && auto_preview_mode)
2065 		goto auto_preview;
2066 
2067 	    cura = article_id;
2068 	    if (!auto_select_closed || !(articles[firsta + cura]->flag & A_CLOSED)) {
2069 		toggle();
2070 		if (!is_k_select && auto_select_subject)
2071 		    goto select_subject;
2072 		mark();
2073 		cura++;
2074 		goto same_prompt;
2075 	    }
2076 	    if (auto_select_closed < 0) {
2077 		article_id = cura;
2078 		goto open_subject;
2079 	    }
2080 	    mi = menu_info + articles[firsta + cura]->menu_line;
2081 	    if (mi->mi_unread == 0)
2082 		repl_attr_subject(A_KILL, A_SELECT, 1);
2083 	    else if (mi->mi_selected == mi->mi_unread)
2084 		repl_attr_subject(auto_select_closed == 2 ? A_KILL : A_SELECT, 0, 1);
2085 	    else
2086 		repl_attr_subject(auto_select_closed == 2 ? A_KILL : 0, A_SELECT, 1);
2087 	    goto same_prompt;
2088 
2089 	case K_SELECT_INVERT:
2090 	    if (numa < 0)
2091 		goto nextmenu;
2092 
2093 	    temp = cura;
2094 
2095 	    no_raw();		/* for x-on/x-off */
2096 	    for (cura = 0; cura <= numa; cura++) {
2097 		toggle();
2098 	    }
2099 	    for (cura = 0; cura <= numa; cura++) {
2100 		if (IS_VISIBLE(articles[firsta + cura]))
2101 		    mark();
2102 	    }
2103 	    fl;
2104 
2105 	    REDRAW_CHECK;
2106 	    nn_raw();
2107 
2108 	    cura = temp;
2109 	    goto same_prompt;
2110 
2111 	case K_UNSELECT_ALL:
2112 	    if (last_k_cmd == K_UNSELECT_ALL)
2113 		repl_attr_all(A_SELECT, 0, 1);
2114 	    else
2115 		repl_attr_all(A_AUTO_SELECT, 0, 1);
2116 	    fl;
2117 	    cura = 0;
2118 	    goto same_prompt;
2119 
2120 	case K_NEXT_LINE:
2121 	    if (numa < 0)
2122 		goto nextmenu;
2123 
2124 	    cura++;
2125 	    goto same_prompt;
2126 
2127 	case K_PREV_LINE:
2128 	    if (numa < 0)
2129 		goto nextmenu;
2130 
2131 	    if (--cura < 0)
2132 		cura = numa;
2133 	    set_root_if_closed();
2134 	    goto same_prompt;
2135 
2136 	case K_SELECT_SUBJECT:
2137 	    if (numa < 0)
2138 		goto nextmenu;
2139 
2140 	    MOUSE_MENU;
2141 
2142 	    if (MOUSE)
2143 		cura = article_id;
2144 
2145 	    if (last_k_cmd != K_ARTICLE_ID)
2146 		toggle();
2147 
2148     select_subject:
2149 	    repl_attr_subject(A_KILL, last_attr, 1);
2150 	    goto same_prompt;
2151 
2152 	case K_SELECT_RANGE:
2153 	    if (numa < 0)
2154 		goto nextmenu;
2155 
2156 	    if (last_k_cmd == K_ARTICLE_ID) {
2157 		cura--;
2158 		if (cura < 0)
2159 		    cura = numa;
2160 	    } else
2161 		last_attr = (articles[firsta + cura]->attr & A_SELECT) ? 0 : A_SELECT;
2162 
2163     range_again:
2164 
2165 	    if (MOUSE) {
2166 		if ((mouse_y < firstl) || (mouse_y > firstl + menu_length - 1))
2167 		    goto same_prompt;
2168 		/* stop selection when down click changes groups */
2169 		if (last_k_cmd == K_INVALID)
2170 		    goto same_prompt;
2171 	    } else {
2172 		prompt("\1%select range\1 %c-", last_attr ? "S" : "Des", ident[cura]);
2173 
2174 		k_cmd = get_k_cmd();
2175 		if (k_cmd == K_SELECT_RANGE) {
2176 		    last_attr = last_attr ? 0 : A_SELECT;
2177 		    goto range_again;
2178 		}
2179 		if (k_cmd != K_ARTICLE_ID)
2180 		    goto Prompt;
2181 
2182 	    }
2183 
2184 	    if (article_id > cura) {
2185 		article_number  tmp = cura;
2186 		cura = article_id;
2187 		article_id = tmp;
2188 	    }
2189 	    repl_attr(firsta + article_id, firsta + cura + 1, A_KILL, last_attr, 1);
2190 	    goto Prompt;
2191 
2192 	case K_AUTO_SELECT:
2193 	    do_auto_select((regexp *) NULL, 1);
2194 	    goto same_prompt;
2195 
2196 	case K_GOTO_MATCH:
2197 	    prompt("\1Select regexp\1 ");
2198 	    if ((fname = get_s(NONE, NONE, NONE, NULL_FCT)) == NULL)
2199 		goto Prompt;
2200 
2201 	    if (*fname != NUL) {
2202 		if (regular_expr)
2203 		    freeobj(regular_expr);
2204 		if (case_fold_search)
2205 		    fold_string(fname);
2206 		regular_expr = regcomp(fname);
2207 	    }
2208 	    if (regular_expr == NULL)
2209 		msg("No previous expression");
2210 	    else
2211 		do_auto_select(regular_expr, 2);
2212 
2213 	    goto Prompt;
2214 
2215 	case K_NEXT_PAGE:
2216 	    if (nexta < n_articles)
2217 		goto nextmenu;
2218 	    if (firsta == 0)
2219 		goto same_prompt;
2220 
2221 	    nexta = 0;
2222 	    goto nextmenu;
2223 
2224 	case K_PREV_PAGE:
2225 	    if (firsta == 0 && nexta == n_articles)
2226 		goto same_prompt;
2227 
2228     prevmenu:
2229 	    nexta = (firsta > 0 ? firsta : n_articles);
2230 
2231 	    for (menu_length = maxa; menu_length > 0 && --nexta >= 0;) {
2232 		if (!IS_VISIBLE(articles[nexta]))
2233 		    continue;
2234 		if (--menu_length > 0) {
2235 		    switch (menu_spacing) {
2236 			case 0:
2237 			    break;
2238 			case 1:
2239 			    if ((articles[nexta]->flag & A_ROOT_ART) == 0)
2240 				break;
2241 			    /* XXX: fall? */
2242 			case 2:
2243 			    --menu_length;
2244 			    break;
2245 		    }
2246 		}
2247 	    }
2248 	    if (nexta < 0)
2249 		nexta = 0;
2250 	    goto nextmenu;
2251 
2252 	case K_FIRST_PAGE:
2253 	    if (firsta == 0)
2254 		goto same_prompt;
2255 
2256 	    nexta = 0;
2257 	    goto nextmenu;
2258 
2259 	case K_LAST_PAGE:
2260 	    if (nexta == n_articles)
2261 		goto same_prompt;
2262 	    firsta = 0;
2263 	    goto prevmenu;
2264 
2265 	case K_PREVIEW:
2266 	    if (numa < 0)
2267 		goto nextmenu;
2268 
2269     preview_other:
2270 
2271 	    MOUSE_MENU;
2272 	    if (!MOUSE) {
2273 		prompt("\1Preview article\1");
2274 		k_cmd = get_k_cmd();
2275 
2276 		if (k_cmd != K_ARTICLE_ID) {
2277 		    if (k_cmd != K_PREVIEW)
2278 			goto Prompt;
2279 		    article_id = cura;
2280 		}
2281 	    }
2282     auto_preview:
2283 	    temp = prompt_line;
2284 
2285     preview_next:
2286 	    cura = article_id;
2287 	    ah = articles[firsta + cura];
2288 
2289 	    no_raw();
2290 	    orig_attr = ah->attr;
2291 	    ah->attr = 0;
2292 	    menu_cmd = more(ah, MM_PREVIEW, prompt_line);
2293 	    if (menu_cmd == MC_MENU) {
2294 		if (ah->attr == 0)
2295 		    ah->attr = orig_attr;
2296 		if (prompt_line < 0) {
2297 		    next_cura = cura;
2298 		    goto redraw;
2299 		}
2300 		mark();
2301 		prompt_line = temp;
2302 		next_cura = -1;
2303 		goto build_prompt;
2304 	    }
2305 	    if (ah->attr == 0)
2306 		ah->attr = preview_mark_read ? A_READ : orig_attr;
2307 
2308 	    if (prompt_line >= 0)
2309 		mark();
2310 	    next_cura = ++cura;
2311 
2312 	    switch (menu_cmd) {
2313 
2314 		case MC_DO_KILL:
2315 		    if (!do_auto_kill())
2316 			break;
2317 		    elim_list[0] = firsta;
2318 		    elim_list[1] = firsta + cura;
2319 		    elim_articles(elim_list, 2);
2320 		    firsta = elim_list[0];
2321 		    next_cura = elim_list[1] - firsta;
2322 		    goto redraw;
2323 
2324 		case MC_DO_SELECT:
2325 		    if (prompt_line >= 0) {	/* not redrawn */
2326 			do_auto_select((regexp *) NULL, 2);
2327 			break;
2328 		    }
2329 		    numa = -1;
2330 		    do_auto_select((regexp *) NULL, 2);
2331 
2332 		    /* FALLTHROUGH */
2333 		case MC_QUIT:
2334 		    menu_return(ME_QUIT);
2335 		    break;	/* for lint. we can't actually get here */
2336 
2337 		case MC_REENTER_GROUP:
2338 		    menu_return(ME_REENTER_GROUP);
2339 		    break;	/* for lint, we can't actually get here */
2340 
2341 		case MC_NEXT:
2342 		case MC_PREVIEW_NEXT:
2343 		    if (prompt_line < 0) {	/* redrawn screen ! */
2344 			if (quit_preview(menu_cmd))
2345 			    goto redraw;
2346 			prompt_line = Lines;
2347 		    } else {
2348 			if (quit_preview(menu_cmd)) {
2349 			    next_cura = -1;
2350 			    break;
2351 			}
2352 			prompt_line = temp;
2353 		    }
2354 		    article_id = cura;
2355 		    goto preview_next;
2356 
2357 		case MC_PREVIEW_OTHER:
2358 		    prompt_line = temp;
2359 		    nn_raw();
2360 		    goto preview_other;
2361 
2362 		default:
2363 		    if (prompt_line < 0)
2364 			goto redraw;
2365 		    break;
2366 	    }
2367 
2368 	    prompt_line = temp;
2369 	    goto build_prompt;
2370 
2371 	case K_LAYOUT:
2372 	    if (++fmt_linenum > 4)
2373 		fmt_linenum = 0;
2374 	    goto redraw;
2375 
2376 	default:
2377 	    msg("Command %d not supported", k_cmd);
2378 	    goto same_prompt;
2379     }
2380 
2381     no_raw();
2382 
2383 do_auto_read:
2384     switch (show_articles()) {
2385 
2386 	case MC_MENU:
2387 	    goto redraw;
2388 
2389 	case MC_READGROUP:
2390 	    if (k_cmd == K_READ_GROUP_THEN_SAME) {
2391 		if (read_ret_next_page && nexta < n_articles)
2392 		    firsta = nexta;
2393 		goto redraw;
2394 	    }
2395 	    /* XXX: fall? */
2396 	case MC_NEXTGROUP:
2397 	    menu_cmd = ME_NEXT;
2398 	    break;
2399 
2400 	case MC_REENTER_GROUP:
2401 	    menu_cmd = ME_REENTER_GROUP;
2402 	    break;
2403 
2404 	case MC_QUIT:
2405 	    menu_cmd = ME_QUIT;
2406 	    break;
2407 
2408 	default:
2409 	    sys_error("show_articles returned improper value");
2410     }
2411 
2412 menu_exit:
2413 
2414     cur_bypass = o_bypass;
2415     n_selected = o_selected;
2416     firsta = o_firsta;
2417     in_menu_mode = o_mode;
2418     menu_level--;
2419 
2420     no_raw();
2421     return menu_cmd;
2422 }
2423 
2424 
2425 /*
2426  *	return article header for article on menu
2427  */
2428 
2429 article_header *
get_menu_article(void)2430 get_menu_article(void)
2431 {
2432     register article_header *ah;
2433 
2434     tprintf(" from article: ");
2435     fl;
2436 
2437     if (get_k_cmd() == K_ARTICLE_ID) {
2438 	ah = articles[firsta + article_id];
2439 	if (ah->a_group)
2440 	    init_group(ah->a_group);
2441 	return ah;
2442     }
2443     return NULL;
2444 }
2445 
2446 
2447 
2448 /*
2449  *	read command from command line
2450  */
2451 
2452 int
alt_command(void)2453 alt_command(void)
2454 {
2455     int             ok_val, macro_cmd;
2456     char           *cmd, brkchars[10];
2457 
2458     if (get_from_macro)
2459 	ok_val = AC_UNCHANGED;
2460     else {
2461 	prompt(":");
2462 	ok_val = AC_PROMPT;
2463     }
2464 
2465 again:
2466 
2467     sprintf(brkchars, "?%c ", erase_key);
2468 
2469     cmd = get_s(NONE, NONE, brkchars, alt_completion);
2470     if (cmd == NULL ||
2471 	*cmd == NUL || *cmd == SP || (key_type) * cmd == erase_key)
2472 	return ok_val;
2473 
2474     macro_cmd = get_from_macro;
2475 
2476     if (*cmd == '?') {
2477 	display_file("help.extended", CLEAR_DISPLAY);
2478 	ok_val = AC_REDRAW;
2479 	goto new_prompt;
2480     }
2481     ok_val = parse_command(cmd, ok_val, (FILE *) NULL);
2482     if (ok_val != AC_REDRAW || !delay_redraw)
2483 	return ok_val;
2484 
2485 new_prompt:
2486     if (macro_cmd)
2487 	return ok_val;
2488 
2489     prompt_line = -1;
2490     tprintf("\n\r:");
2491     fl;
2492     goto again;
2493 }
2494