home *** CD-ROM | disk | FTP | other *** search
/ Unix System Administration Handbook 1997 October / usah_oct97.iso / news / nn.tar / nn-6.5.1 / menu.c < prev    next >
C/C++ Source or Header  |  1996-08-17  |  53KB  |  2,355 lines

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