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