home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume22 / nn6.4 / part06 / menu.c < prev   
Encoding:
C/C++ Source or Header  |  1990-06-07  |  32.2 KB  |  1,578 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 "term.h"
  10. #include "keymap.h"
  11. #include "menu.h"
  12. #include "regexp.h"
  13.  
  14. import char *news_lib_directory;
  15.  
  16. export int  preview_window = 0;    /* size of preview window */
  17. export int  fmt_linenum    = 1; /* menu line format */
  18. export int  fmt_rptsubj    = 0; /* repeat identical subjects if !0 */
  19. export int  novice       = 1; /* novice mode -- use extended prompts */
  20. export int  long_menu       = 0; /* don't put empty lines around menu lines */
  21. export int  delay_redraw   = 0; /* prompt again if :-command clears screen */
  22. export int  slow_mode       = 0;    /* mark selected articles with *s */
  23. export int  re_layout      = 0; /* Re: format presentation on menus */
  24. export int  collapse_subject = 25; /* collapse long subjects at position */
  25. export int  conf_group_entry = 0; /* ask whether group should be entered */
  26. export int  conf_entry_limit = 0; /* ask only if more than .. unread */
  27.  
  28. export int  auto_preview_mode = 0; /* preview rather than select */
  29. export int  preview_continuation = 12; /* what to do after preview */
  30. export int  preview_mark_read = 1; /* previewed articles are A_READ */
  31. export int  select_on_sender = 0; /* + command selects on sender */
  32.  
  33. export char delayed_msg[100] = "";    /* give to msg() after redraw */
  34.  
  35. export int  flush_typeahead = 0;
  36.  
  37. import int also_read_articles;
  38. import int merged_menu;
  39. import int case_fold_search;
  40.  
  41. extern group_completion();
  42.  
  43. static regexp *regular_expr = NULL;
  44.  
  45. static int firstl;    /* first menu line */
  46.  
  47. static article_number firsta;    /* first article on menu (0 based) */
  48. static article_number nexta;    /* first article on next menu */
  49. static int cura;    /* current article */
  50. static int next_cura;    /* article to become cura if >= 0 */
  51. static int numa;    /* no of articles on menu - 1 */
  52. static attr_type last_attr;
  53.  
  54. #define INTERVAL1    ('z' - 'a' + 1)
  55. #define INTERVAL2    ('9' - '0' + 1)
  56.  
  57. char ident[] = "abcdefghijklmnopqrstuvwxyz0123456789";
  58.  
  59. char attributes[30] = " .,+=#! **"; /* Corresponds to A_XXXX in data.h */
  60.  
  61. static prt_replies(level)
  62. {
  63.     int re;
  64.  
  65.     if (level == 0) return 0;
  66.     re = level & 0x80;
  67.     level &= 0x7f;
  68.  
  69.     switch (re_layout) {
  70.      case 1:
  71.     if (!re) return 0;
  72.     so_printf(">");
  73.     return 1;
  74.      case 2:
  75.     so_printf("%d>", level);
  76.     return level < 10 ? 2 : 3;
  77.      case 3:
  78.     so_printf("Re: ");
  79.     return 4;
  80.     }
  81.  
  82.     if (level < 10) {
  83.     so_printf("%-.*s", level, ">>>>>>>>>");
  84.     return level;
  85.     }
  86.  
  87.     so_printf(">>>%3d >>>>", level);
  88.     return 11;
  89. }
  90.  
  91. static mark()
  92. {
  93.     register article_header *ah;
  94.     int lno, lnum, lsubj, lname;
  95.  
  96.     ah = articles[firsta + cura];
  97.     last_attr = ah->attr;
  98.  
  99.     if (last_attr == ah->disp_attr) return;
  100.     if (cura < 0 || cura > numa) return;
  101.  
  102.     lno = firstl + cura;
  103.     if (ah->disp_attr == A_NOT_DISPLAYED) {
  104.     gotoxy(0, lno);
  105.     putchar(ident[cura]);
  106.     goto print_line;
  107.     }
  108.  
  109.     /* A_AUTO_SELECT will not occur here! */
  110.  
  111.     if (!slow_mode)
  112.     if (last_attr == A_SELECT) {
  113.         if ((ah->disp_attr & A_SELECT) == 0) goto print_line;
  114.     } else {
  115.         if (ah->disp_attr & A_SELECT) goto print_line;
  116.     }
  117.  
  118.     gotoxy(1, lno);
  119.     putchar(attributes[ah->attr]);
  120.     goto out;
  121.  
  122.  print_line:
  123.     /* menu line formats:
  124.                 1  3    8 10     20 22        xx
  125.         :  :    :  :      :  :        :
  126.        -1    id name:8 subject
  127.        0    id name           subject     +lines
  128.        1    id name       lines  subject
  129.        2    id  lines  subject
  130.        3    id subject
  131.        4    id   subject  (or as 1 if short subject)
  132.      */
  133.  
  134.     if (fmt_linenum > 4) fmt_linenum = 1;
  135.  
  136.     if (!slow_mode && (ah->attr & A_SELECT)) {
  137.     if (so_gotoxy(1, lno, 1) == 0)
  138.         putchar(attributes[A_SELECT]);
  139.     } else {
  140.         gotoxy(1, lno);
  141.     putchar(attributes[ah->attr]);
  142.     }
  143.  
  144.     if (ah->lines <    10) lnum = 1; else
  145.     if (ah->lines <   100) lnum = 2; else
  146.     if (ah->lines <  1000) lnum = 3; else
  147.     if (ah->lines < 10000) lnum = 4; else lnum = 5;
  148.  
  149.     lsubj = Columns - cookie_size - 2; /* ident char + space */
  150.  
  151.     switch (fmt_linenum) {
  152.  
  153.      case -1:
  154.     lsubj -= 9;
  155.     so_printf("%-8.8s ", ah->sender);
  156.     break;
  157.  
  158.      case 0:
  159.     lsubj -= NAME_LENGTH + 1 + 2 + lnum;  /* name. .subj. +.lines */
  160.     so_printf("%-*s ", NAME_LENGTH, ah->sender);
  161.     break;
  162.  
  163.      case 4:
  164.     if (ah->subj_length > (lsubj - NAME_LENGTH - 5))
  165.         if (fmt_rptsubj || lno == firstl || (ah->flag & A_SAME) == 0) {
  166.         so_printf("  ");
  167.         lsubj -= 2;
  168.         break;
  169.         }
  170.     /* else use layout 1, so fall thru */
  171.  
  172.      case 1:
  173.     lsubj -= NAME_LENGTH + 5;
  174.     /* name.lines.  .subj (name may be shortened) */
  175.     lname = NAME_LENGTH + 2 - lnum;
  176.     so_printf("%-*.*s ", lname, lname, ah->sender);
  177.     so_printf(ah->lines >= 0 ? "%d  " : "?  ", ah->lines);
  178.     break;
  179.  
  180.      case 2:
  181.     lsubj -= 6;
  182.     so_printf("%5d ", ah->lines);
  183.     break;
  184.  
  185.      case 3:
  186.     break;
  187.     }
  188.  
  189.     if (!fmt_rptsubj && lno > firstl && ah->flag & A_SAME) {
  190.     if (ah->replies == 0 || prt_replies(ah->replies) == 0)
  191.         so_printf("-");
  192.     } else {
  193.     lsubj -= prt_replies(ah->replies);
  194.     if (lsubj >= ah->subj_length)
  195.         so_printf("%s", ah->subject);
  196.     else
  197.     if (collapse_subject < 0)
  198.         so_printf("%-.*s", lsubj, ah->subject);
  199.     else {
  200.         if (collapse_subject > 0)
  201.         so_printf("%-.*s", collapse_subject, ah->subject);
  202.         lsubj -= 2 + collapse_subject;
  203.         so_printf("<>%s", ah->subject + ah->subj_length - lsubj);
  204.     }
  205.     }
  206.  
  207.     if (fmt_linenum == 0)
  208.     so_printf(ah->lines >= 0 ? " +%d" : " +?", ah->lines);
  209.  
  210.     so_end();
  211.  
  212.  out:
  213.     ah->disp_attr = last_attr;
  214.     return;
  215. }
  216.  
  217. static new_mark(how)
  218. attr_type how;
  219. {
  220.     articles[firsta + cura]->attr = how;
  221.     mark();
  222. }
  223.  
  224. static toggle()
  225. {
  226.     last_attr = articles[firsta + cura]->attr =
  227.     articles[firsta + cura]->attr & A_SELECT ? 0 : A_SELECT;
  228. }
  229.  
  230. static do_auto_kill()
  231. {
  232.     register article_number i;
  233.     register article_header *ah, **ahp;
  234.     int any = 0;
  235.  
  236.     for (i = 0, ahp = articles; i < n_articles; i++, ahp++) {
  237.     ah = *ahp;
  238.     if (auto_select_article(ah, 0)) {
  239.         ah->attr = A_KILL;
  240.         any = 1;
  241.     }
  242.     }
  243.     return any;
  244. }
  245.  
  246. /*
  247.  *    perform auto selections that are not already selected
  248.  *    if article is in range firsta..firsta+numa (incl) mark article
  249.  */
  250.  
  251. static do_auto_select(re, mode)
  252. regexp *re;
  253. int mode;
  254. {
  255.     register article_number i;
  256.     register article_header *ah, **ahp;
  257.     int count = 0, o_cura;
  258.  
  259.     o_cura = cura;
  260.  
  261.     for (i = 0, ahp = articles; i < n_articles; i++, ahp++) {
  262.     ah = *ahp;
  263.     if (re != NULL) {
  264.         if (!regexec_cf(re, select_on_sender ? ah->sender : ah->subject)) continue;
  265.     } else
  266.         if (!auto_select_article(ah, mode)) continue;
  267.  
  268.     count++;
  269.     if (ah->attr & A_SELECT) continue;
  270.     if (firsta <= i && i <= (firsta+numa)) {
  271.         cura = i - firsta;
  272.         new_mark(A_SELECT);
  273.     } else
  274.         ah->attr = A_SELECT;
  275.     }
  276.  
  277.     if (count)
  278.     msg("Selected %d article%s", count, plural((long)count));
  279.     else
  280.     msg("No selections");
  281.     
  282.     cura = o_cura;
  283. }
  284.  
  285. static quit_preview(cmd)
  286. int cmd;
  287. {
  288.     int op;
  289.  
  290.     if ((firsta + cura) >= n_articles) return 1;
  291.     op = preview_continuation;
  292.     if (cmd == MC_PREVIEW_NEXT)    op /= 10;
  293.     op %= 10;
  294.     switch (op) {
  295.      case 0:
  296.     return 1;
  297.      case 1:
  298.     return 0;
  299.      case 2:
  300.     return (articles[firsta+cura]->flag & (A_SAME | A_ALMOST_SAME)) == 0;
  301.     }
  302.     return 0;
  303. }
  304.  
  305.  
  306. static show_articles()
  307. {
  308.     register article_number cur, next, temp;
  309.     register article_header *ah;
  310.     article_number elim_list[1];
  311.     register int mode;
  312.     int cmd, prev = -1, again;
  313.     attr_type o_attr;
  314.  
  315.     do {
  316.     for (cur = 0; cur < n_articles; cur++) {
  317.         if (articles[cur]->attr & A_SELECT) break;
  318.     }
  319.  
  320.     while (cur < n_articles) {
  321.  
  322.         for (next = cur+1; next < n_articles; next++) {
  323.         if (articles[next]->attr & A_SELECT) break;
  324.         }
  325.  
  326.      show:
  327.         ah = articles[cur];
  328.         o_attr = ah->attr;
  329.         ah->attr = 0;
  330.  
  331.         mode = 0;
  332.         if (prev >= 0) mode |= MM_PREVIOUS;
  333.         if (next == n_articles) mode |= MM_LAST_SELECTED;
  334.         if ((cur + 1) >= n_articles) mode |= MM_LAST_ARTICLE;
  335.         if (cur == 0) mode |= MM_FIRST_ARTICLE;
  336.  
  337.         cmd = more(ah, mode, 0);
  338.  
  339.         switch (cmd) {
  340.  
  341.          case MC_DO_KILL:
  342.         if (do_auto_kill()) {
  343.             elim_list[0] = next;
  344.             elim_articles(elim_list, 1);
  345.             cur = elim_list[0];
  346.             /* if next was n_articles, cur will be 0 */
  347.             if (cur >= n_articles || cur < 0
  348.             || (articles[cur]->attr & A_SELECT) == 0)
  349.             cur = n_articles;
  350.             continue;
  351.         }
  352.         break;
  353.  
  354.          case MC_DO_SELECT:
  355.         for (temp = cur+1; temp < n_articles; temp++) {
  356.             if (auto_select_article(ah = articles[temp], 2)) {
  357.             ah->attr = A_SELECT;
  358.             if (temp < next) next = temp;
  359.             }
  360.         }
  361.         break;
  362.  
  363.          case MC_PREV:
  364.         if (prev == next) break;
  365.  
  366.         ah->attr = o_attr;
  367.         next = cur; cur = prev; prev = next;
  368.         goto show;
  369.  
  370.          case MC_NEXTSUBJ:
  371.         ah->attr = A_READ;
  372.         for (next = cur+1; next < n_articles; next++) {
  373.             if (((ah = articles[next])->flag & (A_SAME | A_ALMOST_SAME)) == 0) break;
  374.             ah->attr = A_READ;
  375.         }
  376.         for (; next < n_articles; next++) {
  377.             if (articles[next]->attr & A_SELECT) break;
  378.         }
  379.         break;
  380.  
  381.          case MC_ALLSUBJ:
  382.         for (next = cur+1; next < n_articles; next++) {
  383.             ah = articles[next];
  384.             if ((ah->flag & (A_SAME | A_ALMOST_SAME)) == 0) break;
  385.             ah->attr = A_SELECT;
  386.         }
  387.         for (next = cur+1; next < n_articles; next++)
  388.             if (articles[next]->attr & A_SELECT) break;
  389.         break;
  390.  
  391.          case MC_MENU:
  392.         ah->attr = o_attr;
  393.         if (nexta - firsta < n_articles)
  394.             if ((firsta = cur - 5) < 0) firsta = 0;
  395.         next_cura = cur - firsta;
  396.  
  397.         return MC_MENU;
  398.  
  399.          case MC_NEXT:
  400.         if (ah->attr == 0)    /* Not set by more (sufficient ???) */
  401.             ah->attr = A_READ;
  402.         break;
  403.  
  404.          case MC_BACK_ART:
  405.         ah->attr = o_attr ? o_attr : A_SEEN;
  406.         next = cur - 1;
  407.         break;
  408.  
  409.          case MC_FORW_ART:
  410.         ah->attr = o_attr ? o_attr : A_SEEN;
  411.         next = cur + 1;
  412.         break;
  413.  
  414.          case MC_NEXTGROUP:
  415.          case MC_REENTER_GROUP:
  416.          case MC_QUIT:
  417.         ah->attr = o_attr;
  418.         return cmd;
  419.  
  420.          case MC_READGROUP:
  421.         return cmd;
  422.         }
  423.  
  424.         prev = cur; cur = next;
  425.     }
  426.  
  427.     for (cur = 0; cur < n_articles; cur++)
  428.         if (articles[cur]->attr & A_SELECT) break;
  429.     if (cur < n_articles) continue;
  430.  
  431.     again = 0;
  432.     for (cur = 0; cur < n_articles; cur++) {
  433.         ah = articles[cur];
  434.         if (ah->attr == A_LEAVE) {
  435.         if (again == 0) {
  436.             prompt("Show left over articles again now? ");
  437.             if (yes(0) <= 0) break;
  438.         }
  439.         ah->attr = A_SELECT;
  440.         again++;
  441.         }
  442.     }
  443.  
  444.     if (again > 1)
  445.         sprintf(delayed_msg, "Showing %ld articles again", again);
  446.     } while (again);
  447.  
  448.     return MC_READGROUP;
  449. }
  450.  
  451. static int article_id;
  452. static int cur_key;
  453.  
  454. static int get_k_cmd()
  455. {
  456.     extern int any_message;
  457.     register int c, map;
  458.  
  459.     if (flush_typeahead) flush_input();
  460.  
  461.  loop:
  462.  
  463.     article_id = -1;
  464.  
  465.     if ((c = get_c()) & GETC_COMMAND) {
  466.     cur_key = K_interrupt;
  467.     map = c & ~GETC_COMMAND;
  468.     } else {
  469.     cur_key = c;
  470.     map = menu_key_map[c];
  471.     }
  472.     if (s_hangup) map = K_QUIT;
  473.  
  474.     if (map & K_MACRO) {
  475.     m_invoke(map & ~K_MACRO);
  476.     goto loop;
  477.     }
  478.  
  479.     if (map & K_ARTICLE_ID) {
  480.     article_id = map & ~K_ARTICLE_ID;
  481.     map = K_ARTICLE_ID;
  482.  
  483.     if (article_id < 0 || article_id > numa) {
  484.         ding();
  485.         goto loop;
  486.     }
  487.     }
  488.  
  489.     if (any_message) clrmsg(-1);
  490.     return map;
  491. }
  492.  
  493.  
  494. char *pct(start, end, first, last)
  495. long start, end, first, last;
  496. {
  497.     long n = end - start;
  498.     static char buf[16];
  499.     char *fmt;
  500.  
  501.     if (first <= start || n <= 0)
  502.     if (last >= end || n <= 0)
  503.         return "All";
  504.     else
  505.         fmt = "Top %d%%";
  506.     else
  507.     if (last >= end)
  508.         return "Bot";
  509.     else
  510.         fmt = "%d%%";
  511.  
  512.     sprintf(buf, fmt, ((last - start) * 100)/n);
  513.     return buf;
  514. }
  515.  
  516. static repl_attr(first, last, old, new, update)
  517. register article_number first, last;
  518. register attr_type old, new;
  519. int update;
  520. {
  521.     int any;
  522.  
  523.     if (new == old) return 0;
  524.     if (new == A_KILL) update = 0;
  525.  
  526.     any = 0;
  527.     while (first < last) {
  528.     if (old == A_KILL || articles[first]->attr == old) {
  529.         articles[first]->attr = new;
  530.         if (update) {
  531.         cura = first-firsta;
  532.         mark();
  533.         }
  534.         any = 1;
  535.     }
  536.  
  537.     first++;
  538.     }
  539.     return any;
  540. }
  541.  
  542.  
  543. static repl_attr_all(old, new, update)
  544. attr_type old, new;
  545. int update;
  546. {
  547.     return repl_attr((article_number)0, n_articles, old, new, update);
  548. }
  549.  
  550. static get_purpose(purpose)
  551. char *purpose;
  552. {
  553. #ifdef NNTP
  554.     return;            /* newsgroups file is not available */
  555. #else
  556.     FILE *f;
  557.     char line[256], group[80];
  558.     register char *cp, *pp;
  559.     register int len;
  560.  
  561.     f = open_file(relative(news_lib_directory, "newsgroups"), OPEN_READ);
  562.     if (f == NULL) return;
  563.  
  564.     sprintf(group, "%s\t", current_group->group_name);
  565.     len = current_group->group_name_length + 1;
  566.  
  567.     while (fgets(line, 256, f) != NULL) {
  568.     if (strncmp(line, group, len)) continue;
  569.     cp = line + len;
  570.     while (*cp && isspace(*cp)) cp++;
  571.     for (pp = purpose, len = 76; --len >= 0 && *cp && *cp != NL; )
  572.         *pp++ = *cp++;
  573.     *pp = NUL;
  574.     }
  575.  
  576.     fclose(f);
  577. #endif
  578. }
  579.  
  580.  
  581. menu(print_header)
  582. fct_type print_header;
  583. {
  584.     register         k_cmd, cur_k_cmd;
  585.     register        article_header *ah;
  586.     int            last_k_cmd;
  587.     int         menu_cmd, temp;
  588.     int         save_selected;
  589.     article_number    last_save;
  590.     attr_type        orig_attr, junk_attr;
  591.     int            doing_unshar, did_unshar, junk_prompt;
  592.     char         *fname, *savemode, *init_save();
  593.     int         maxa;    /* max no of articles per menu page */
  594.     int         o_firsta, o_mode;    /* for recursive calls */
  595.     static        menu_level = 0;
  596.     char        purpose[80], pr_fmt[60];
  597.     extern int         enable_stop, file_completion();
  598.     extern int        alt_cmd_key, in_menu_mode;
  599.     article_number    elim_list[3];
  600.     int            entry_check;
  601.  
  602. #define    menu_return(cmd) \
  603.     { menu_cmd = (cmd); goto menu_exit; }
  604.  
  605.     flush_input();
  606.  
  607.     o_firsta = firsta;
  608.     o_mode = in_menu_mode;
  609.     in_menu_mode = 1;
  610.  
  611.     menu_level++;
  612.  
  613.     entry_check = menu_level == 1 && conf_group_entry &&
  614.     current_group->unread_count > conf_entry_limit;
  615.  
  616.     sprintf(pr_fmt,
  617.         menu_level == 1 ?
  618.           "\1\2-- SELECT %s-----%%s-----\1" :
  619.           "\1\2-- SELECT %s-----%%s-----<%s%d>--\1",
  620.           novice ? "-- help:? " : "",
  621.           novice ? "level " : "",
  622.           menu_level);
  623.  
  624.     purpose[0] = NUL;
  625.     if (!merged_menu && current_group->group_flag & G_NEW)
  626.     get_purpose(purpose);
  627.  
  628.     firsta = 0;
  629.     while (firsta < n_articles && articles[firsta]->attr == A_SEEN)
  630.     firsta++;
  631.  
  632.     if (firsta == n_articles) firsta = 0;
  633.  
  634.     next_cura = -1;
  635.     cur_k_cmd = K_UNBOUND;
  636.  
  637. #ifdef HAVE_JOBCONTROL
  638. #define    REDRAW_CHECK    if (s_redraw) goto do_redraw
  639.  
  640.  do_redraw:
  641.     /* safe to clear here, because we are going to redraw anyway */
  642.     s_redraw = 0;
  643. #else
  644. #define REDRAW_CHECK
  645. #endif
  646.  
  647.  redraw:
  648.     s_keyboard = 0;
  649.  
  650.  empty_menu_hack:    /* do: "s_keyboard=1; goto empty_menu_hack;" */
  651.     if (!slow_mode) s_keyboard = 0;
  652.  
  653.     nexta = firsta;
  654.  
  655.     clrdisp();
  656.  
  657.     firstl = CALL(print_header)();
  658.  
  659.     if (entry_check && menu_level == 1) {
  660.     /* we do it here to avoid redrawing the group header */
  661.     entry_check = 0;
  662.     prompt_line = firstl;
  663.     prompt("\1Enter?\1 ");
  664.     if ((temp = yes(0)) <= 0) {
  665.         if (temp < 0) {
  666.         prompt("\1Mark as read?\1 ");
  667.         if ((temp = yes(0)) < 0) menu_return(ME_QUIT);
  668.         if (temp > 0) repl_attr_all(A_KILL, A_READ, 0);
  669.         }
  670.         menu_return(ME_NEXT);
  671.     }
  672.  
  673.     gotoxy(0, firstl);
  674.     clrline();
  675.     }
  676.  
  677.     maxa = Lines - preview_window - firstl - 2;
  678.     if (!long_menu) firstl++, maxa -= 2;
  679.  
  680.     if (maxa > (INTERVAL1 + INTERVAL2))
  681.     maxa = INTERVAL1 + INTERVAL2;
  682.  
  683.  nextmenu:
  684.  
  685.     no_raw();
  686.     gotoxy(0, firstl);
  687.     clrpage(firstl);
  688.  
  689.     if (nexta > 0) {
  690.     firsta = nexta;
  691.     } else
  692.     if (purpose[0]) {
  693.         msg(purpose);
  694.     }
  695.  
  696.     firsta = nexta;
  697.     numa = Lines; /* for mark; is set correctly below */
  698.     cura = 0;
  699.  
  700.     REDRAW_CHECK;
  701.  
  702.     if (!s_keyboard)
  703.     while (nexta < n_articles && cura < maxa) {
  704.         REDRAW_CHECK;
  705.  
  706.         articles[firsta+cura]->disp_attr = A_NOT_DISPLAYED;
  707.         mark();
  708.         nexta++; cura++;
  709.     }
  710.  
  711.     fl;
  712.     s_keyboard = 0;
  713.  
  714.     prompt_line = firstl + cura;
  715.     if (!long_menu || cura < maxa) prompt_line++;
  716.  
  717.     numa = nexta - firsta - 1;
  718.      if (numa < 0) prompt_line++;
  719.  
  720.      if (next_cura >= 0) {
  721.      cura = next_cura;
  722.      next_cura = -1;
  723.      } else {
  724.      cura = 0;
  725.      for (article_id = firsta; cura < numa; article_id++, cura++)
  726.          if ((articles[article_id]->attr & A_SELECT) == 0) break;    /*???*/
  727.      }
  728.  
  729.   build_prompt:
  730.  
  731.      raw();
  732.  
  733.   Prompt:
  734.  
  735.      prompt(pr_fmt,
  736.         pct(0L, (long)(n_articles-1), (long)firsta, (long)(firsta+numa)));
  737.  
  738.      if (delayed_msg[0] != NUL) {
  739.      msg(delayed_msg);
  740.      delayed_msg[0] = NUL;
  741.      }
  742.  
  743.   same_prompt:
  744.  
  745.      if (cura < 0 || cura > numa) cura = 0;
  746.  
  747.      if (numa >= 0) {
  748.      gotoxy(0, firstl + cura);
  749.      fl; /* place cursor at current article id */
  750.      save_xy();
  751.      }
  752.  
  753.      last_k_cmd = cur_k_cmd;
  754.      k_cmd = get_k_cmd();
  755.  
  756.   alt_key:
  757.  
  758.      switch (cur_k_cmd = k_cmd) {
  759.  
  760.       case K_UNBOUND:
  761.      ding();
  762.      flush_input();
  763.       case K_INVALID:
  764.      goto same_prompt;
  765.  
  766.       case K_REDRAW:
  767.      next_cura = cura;
  768.      goto redraw;
  769.  
  770.       case K_LAST_MESSAGE:
  771.      msg((char *)NULL);
  772.      goto same_prompt;
  773.  
  774.       case K_HELP:
  775.      if (numa < 0)  goto nextmenu;    /* give specific help here */
  776.      display_help("menu");
  777.      goto redraw;
  778.  
  779.       case K_SHELL:
  780.      if (group_file_name) *group_file_name = NUL;
  781.      if (shell_escape()) goto redraw;
  782.      goto Prompt;
  783.  
  784.       case K_VERSION:
  785.      prompt(P_VERSION);
  786.      goto same_prompt;
  787.  
  788.       case K_EXTENDED_CMD:
  789.      switch (alt_command()) {
  790.  
  791.       case AC_UNCHANGED:
  792.          goto same_prompt;
  793.  
  794.       case AC_QUIT:
  795.          menu_return( ME_QUIT );
  796.  
  797.       case AC_PROMPT:
  798.          goto Prompt;
  799.  
  800.       case AC_REORDER:
  801.          firsta = 0;
  802.          /* fall thru */
  803.       case AC_REDRAW:
  804.          goto redraw;
  805.  
  806.       case AC_KEYCMD:
  807.          k_cmd = alt_cmd_key;
  808.          goto alt_key;
  809.  
  810.       case AC_REENTER_GROUP:
  811.          menu_return(ME_REENTER_GROUP);
  812.      }
  813.  
  814.       case K_QUIT:
  815.      menu_return(ME_QUIT);
  816.  
  817.       case K_CANCEL:
  818.      savemode = "Cancel";
  819.      fname = "";
  820.      goto cancel1;
  821.  
  822.       case K_SAVE_NO_HEADER:
  823.       case K_SAVE_SHORT_HEADER:
  824.       case K_SAVE_FULL_HEADER:
  825.       case K_PRINT:
  826.       case K_UNSHAR:
  827.       case K_PATCH:
  828.       case K_UUDECODE:
  829.  
  830.      if (numa < 0) goto nextmenu;
  831.  
  832.      fname = init_save(k_cmd, &savemode);
  833.      if (fname == NULL) goto Prompt;
  834.  
  835.       cancel1:
  836.      enable_stop = 0;
  837.      save_selected = 0;
  838.      doing_unshar = k_cmd == K_UNSHAR || k_cmd == K_PATCH;
  839.      did_unshar = 0;
  840.  
  841.      m_startinput();
  842.  
  843.      if (novice)
  844.          msg(" * selected articles on this page, + all selected articles");
  845.  
  846.      while (!save_selected && !did_unshar) {
  847.          prompt("\1%s\1 %.*s Article (* +): ",
  848.             savemode, Columns - 25, fname);
  849.  
  850.          k_cmd = get_k_cmd();
  851.  
  852.          if (k_cmd == K_SELECT_SUBJECT) {
  853.          save_selected = 1;
  854.          cura = 0;
  855.          article_id = firsta;
  856.          last_save = firsta + numa;
  857.          } else
  858.          if (k_cmd == K_AUTO_SELECT) {
  859.          save_selected = 2;
  860.          cura = -firsta;
  861.          article_id = 0;
  862.          last_save = n_articles - 1;
  863.          } else
  864.          if (k_cmd == K_ARTICLE_ID) {
  865.          cura = article_id;
  866.          article_id += firsta;
  867.          last_save = article_id;
  868.          } else
  869.          break;
  870.  
  871.          for ( ; article_id <= last_save ; article_id++, cura++) {
  872.          ah = articles[article_id];
  873.          if (save_selected && (ah->attr & A_SELECT) == 0) continue;
  874.  
  875.          if (cur_k_cmd == K_CANCEL) {
  876.              if (current_group->group_flag & G_FOLDER) {
  877.              if (ah->attr != A_CANCEL) fcancel(ah);
  878.              } else
  879.              switch (cancel(ah)) {
  880.               case -1:
  881.                  did_unshar = 1;
  882.                  continue;
  883.               case 0:
  884.                  ah->attr = A_CANCEL;
  885.                  break;
  886.               default:
  887.                  continue;
  888.              }
  889.  
  890.              if (!did_unshar)
  891.              mark();
  892.  
  893.              continue;
  894.          }
  895.  
  896.          if (doing_unshar) {
  897.              did_unshar++;
  898.          } else
  899.          if (ah->subject != NULL)
  900.              prompt("Processing '%.50s'...", ah->subject);
  901.          else if (cura >= 0 && cura <= numa)
  902.              prompt("Processing %c...", ident[cura]);
  903.          else
  904.              prompt("Processing entry %d...", article_id);
  905.  
  906.          if (save(ah)) {
  907.              ah->attr = A_READ;
  908.              if (doing_unshar) continue;
  909.  
  910.              if (cura >= 0 && cura <= numa)
  911.              mark();
  912.          }
  913.          }
  914.      }
  915.  
  916.      if (save_selected) cura = 0;
  917.  
  918.      m_endinput();
  919.  
  920.      enable_stop = 1;
  921.      if (cur_k_cmd != K_CANCEL)
  922.          end_save();
  923.  
  924.      if (did_unshar) {
  925.          printf("\r\n");
  926.          any_key(0);
  927.          goto redraw;
  928.      }
  929.      goto Prompt;
  930.  
  931.       case K_FOLLOW_UP:
  932. #ifdef NNTP_POST
  933.      if (use_nntp && nntp_no_post()) goto same_prompt;
  934. #endif
  935.       case K_REPLY:
  936.      if (numa < 0) goto nextmenu;
  937.  
  938.      prompt(k_cmd == K_REPLY ?
  939.         "\1Reply to author\1 of article: " :
  940.         "\1Follow Up\1 to article: ");
  941.  
  942.      if (get_k_cmd() == K_ARTICLE_ID)
  943.          if (answer(articles[firsta+article_id], k_cmd, -1))
  944.          goto redraw;
  945.  
  946.      goto Prompt;
  947.  
  948.       case K_POST:
  949.  
  950. #ifdef NNTP_POST
  951.      if (use_nntp && nntp_no_post())
  952.          goto same_prompt;
  953. #endif
  954.      if (post_menu()) goto redraw;
  955.      goto Prompt;
  956.  
  957.       case K_MAIL_OR_FORWARD:
  958.      if (numa < 0) goto nextmenu;
  959.  
  960.      prompt("\1Article to be forwarded\1 (SP if none): ");
  961.  
  962.      if ((k_cmd = get_k_cmd()) == K_ARTICLE_ID) {
  963.          if (answer(articles[firsta+article_id], K_MAIL_OR_FORWARD, 1))
  964.          goto redraw;
  965.      } else
  966.      if (k_cmd == K_CONTINUE)
  967.          if (answer((article_header *)NULL, K_MAIL_OR_FORWARD, 0))
  968.          goto redraw;
  969.  
  970.      goto Prompt;
  971. /*
  972.       case K_CANCEL:
  973.      if (numa < 0) goto nextmenu;
  974.  
  975.      if (current_group->group_flag & G_FOLDER) {
  976.          prompt("\1Cancel Folder\1 Article: ");
  977.          if (get_k_cmd() == K_ARTICLE_ID) {
  978.          cura = article_id;
  979.          fcancel(articles[firsta+article_id]);
  980.          mark();
  981.          }
  982.          goto Prompt;
  983.      }
  984.  
  985.      prompt("\1Cancel\1 Article: ");
  986.  
  987.      if (get_k_cmd() == K_ARTICLE_ID)
  988.          if (cancel(articles[firsta+article_id]) & 1) goto redraw;
  989.      goto Prompt;
  990. */
  991.       case K_UNSUBSCRIBE:
  992.      if (unsubscribe(current_group)) {
  993.          if (current_group->group_flag & G_UNSUBSCRIBED)
  994.          menu_return(ME_NEXT);
  995.          home();
  996.          CALL(print_header)();
  997.      }
  998.      goto Prompt;
  999.  
  1000.       case K_GROUP_OVERVIEW:
  1001.      group_overview(-1);
  1002.      goto redraw;
  1003.  
  1004.       case K_KILL_HANDLING:
  1005.      switch (kill_menu((article_header *)NULL)) {
  1006.       case 0:        /* select */
  1007.          do_auto_select((regexp *)NULL, 2);
  1008.          break;
  1009.       case 1:        /* kill */
  1010.          if (!do_auto_kill()) break;
  1011.          goto junk_killed_articles;
  1012.       default:
  1013.          break;
  1014.      }
  1015.      goto Prompt;
  1016.  
  1017.       case K_CONTINUE:    /* goto next menu page or show the articles */
  1018.      repl_attr(firsta, nexta, 0, A_SEEN, 0);
  1019.      /* fall thru */
  1020.       case K_CONTINUE_NO_MARK:    /* but don't mark unselected articles */
  1021.      if (nexta < n_articles) goto nextmenu;
  1022.      break;
  1023.  
  1024.       case K_READ_GROUP_UPDATE:
  1025.      repl_attr_all(0, A_SEEN, 0);
  1026.      break;
  1027.  
  1028.       case K_READ_GROUP_THEN_SAME:
  1029.      break;
  1030.  
  1031.       case K_NEXT_GROUP_NO_UPDATE:
  1032.      menu_return(ME_NEXT);
  1033.  
  1034.       case K_PREVIOUS:
  1035.      menu_return(ME_PREV);
  1036.  
  1037.       case K_ADVANCE_GROUP:
  1038.       case K_BACK_GROUP:
  1039.      if (merged_menu) {
  1040.          msg("No possible on merged menu");
  1041.          goto same_prompt;
  1042.      }
  1043.      /* FALL THRU */
  1044.  
  1045.       case K_GOTO_GROUP:
  1046.  
  1047.      switch (goto_group(k_cmd, (article_header *)NULL, (flag_type)0)) {
  1048.  
  1049.       case ME_REDRAW:
  1050.          firsta = 0;
  1051.          goto redraw;
  1052.  
  1053.       case ME_NO_ARTICLES:
  1054.          msg("No selections made.");
  1055.  
  1056.       case ME_NO_REDRAW:
  1057.          goto Prompt;
  1058.  
  1059.       case ME_QUIT:
  1060.          menu_return( ME_QUIT );
  1061.  
  1062.       case ME_PREV:
  1063.          goto redraw;
  1064.  
  1065.       case ME_NEXT:
  1066.          s_keyboard = 1;
  1067.          goto empty_menu_hack;
  1068.      }
  1069.  
  1070.       case K_LEAVE_NEXT:
  1071.       case K_JUNK_ARTICLES:
  1072.      junk_prompt = cur_k_cmd == K_JUNK_ARTICLES ? 1 : 5;
  1073.  
  1074.      for (;;) {
  1075.          switch (junk_prompt) {
  1076.           case 1:
  1077.          if (novice) msg("Use J repeatedly to select other functions");
  1078.          prompt("\1Mark read\1 S)een U)nmarked A)ll *+)selected a-z . [LN]");
  1079.          junk_attr = A_READ;
  1080.          break;
  1081.           case 2:
  1082.          prompt("\1Unmark\1 S)een R)ead a-z [*+LAN.J] ");
  1083.          junk_attr = 0;
  1084.          break;
  1085.           case 3:
  1086.          prompt("\1Select\1 L)eft-over, N(leave-next) [USRa-z.J]");
  1087.          junk_attr = A_SELECT;
  1088.          break;
  1089.           case 4:
  1090.          prompt("\1Kill\1 R)ead S)een [LANU*+a-z.J]");
  1091.          junk_attr = A_KILL;
  1092.          break;
  1093.           case 5:
  1094.          prompt("\1Leave\1 a-z .,/ * + U)nmarked [LANRSJ]");
  1095.          junk_attr = A_LEAVE_NEXT;
  1096.          break;
  1097.           default:
  1098.          junk_prompt = 1;
  1099.          continue;
  1100.          }
  1101.  
  1102.       junk_another:
  1103.          if (cura < 0 || cura > numa) cura = 0;
  1104.          gotoxy(0, firstl + cura); fl;
  1105.          
  1106.          switch (get_k_cmd()) {
  1107.           case K_JUNK_ARTICLES:
  1108.          junk_prompt++;    /* can be 0 */
  1109.          continue;
  1110.  
  1111.           case K_ARTICLE_ID:
  1112.          cura = article_id;
  1113.           case K_SELECT:
  1114.          if (junk_attr == A_KILL) junk_attr = A_READ;
  1115.          articles[firsta + cura]->attr = junk_attr;
  1116.          mark();
  1117.          cura++;
  1118.          goto junk_another;
  1119.  
  1120.           case K_NEXT_LINE:
  1121.          cura++;
  1122.          goto junk_another;
  1123.          
  1124.           case K_PREV_LINE:
  1125.          --cura;
  1126.          goto junk_another;
  1127.  
  1128.           case K_SELECT_SUBJECT:
  1129.          if (junk_attr == A_KILL) junk_attr = A_READ;
  1130.          repl_attr(firsta, nexta, A_AUTO_SELECT, A_SELECT, 0);
  1131.          repl_attr(firsta, nexta, A_SELECT, junk_attr, 1);
  1132.          goto Prompt;
  1133.  
  1134.           case K_AUTO_SELECT:
  1135.          repl_attr_all(A_AUTO_SELECT, A_SELECT, 0);
  1136.          orig_attr = A_SELECT;
  1137.          break;
  1138.  
  1139.           default:
  1140.          switch (cur_key) {
  1141.           case 'S':
  1142.              orig_attr = A_SEEN;
  1143.              break;
  1144.  
  1145.           case 'U':
  1146.              orig_attr = 0;
  1147.              break;
  1148.  
  1149.           case 'L':
  1150.              if (junk_attr == A_KILL) junk_attr = A_READ;
  1151.              orig_attr = A_LEAVE;
  1152.              break;
  1153.  
  1154.           case 'A':
  1155.              orig_attr = A_KILL;
  1156.              break;
  1157.  
  1158.           case 'N':
  1159.              orig_attr = A_LEAVE_NEXT;
  1160.              break;
  1161.  
  1162.           case 'R':        /* kill read articles */
  1163.              orig_attr = A_READ;
  1164.              break;
  1165.  
  1166.           default:
  1167.              goto Prompt;
  1168.          }
  1169.          break;
  1170.          }
  1171.          break;
  1172.      }
  1173.      if (nexta - firsta < n_articles) {
  1174.          prompt("On all menu pages? ");
  1175.          switch (yes(1)) {
  1176.           case -1:
  1177.          goto Prompt;
  1178.           case 0:
  1179.          if (!repl_attr(firsta, nexta, orig_attr, junk_attr, 1))
  1180.              goto Prompt;
  1181.          break;
  1182.           case 1:
  1183.          if (!repl_attr_all(orig_attr, junk_attr, 1))
  1184.              goto Prompt;
  1185.          break;
  1186.          }
  1187.      } else
  1188.          if (!repl_attr(firsta, nexta, orig_attr, junk_attr, 1))
  1189.          goto Prompt;
  1190.  
  1191.      if (junk_attr != A_KILL) goto Prompt;
  1192.  
  1193.       junk_killed_articles:
  1194.      elim_list[0] = firsta;
  1195.      elim_list[1] = firsta + cura;
  1196.      elim_list[2] = nexta;
  1197.      if (elim_articles(elim_list, 3)) {
  1198.          firsta = elim_list[0];
  1199.          goto redraw;
  1200.      }
  1201.      firsta = elim_list[0];
  1202.      cura   = elim_list[1] - firsta;
  1203.      nexta  = elim_list[2];
  1204.      goto Prompt;
  1205.  
  1206.       case K_ARTICLE_ID:
  1207.      if (numa < 0) goto nextmenu;
  1208.  
  1209.      if (auto_preview_mode) goto auto_preview;
  1210.  
  1211.      cura = article_id;
  1212.      toggle();
  1213.      mark();
  1214.      cura++;
  1215.  
  1216.      goto same_prompt;
  1217.  
  1218.       case K_SELECT_INVERT:
  1219.      if (numa < 0) goto nextmenu;
  1220.  
  1221.      temp = cura;
  1222.  
  1223.      no_raw();    /* for x-on/x-off */
  1224.      for (cura = 0; cura <= numa; cura++) {
  1225.          toggle();
  1226.          mark();
  1227.      }
  1228.      fl;
  1229.  
  1230.      REDRAW_CHECK;
  1231.      raw();
  1232.  
  1233.      cura = temp;
  1234.      goto same_prompt;
  1235.  
  1236.  
  1237.       case K_SELECT:
  1238.      if (numa < 0) goto nextmenu;
  1239.  
  1240.      toggle();
  1241.      mark();
  1242.      cura++;
  1243.      goto same_prompt;
  1244.  
  1245.       case K_UNSELECT_ALL:
  1246.      if (last_k_cmd == K_UNSELECT_ALL)
  1247.          repl_attr_all(A_SELECT, 0, 1);
  1248.      else
  1249.          repl_attr_all(A_AUTO_SELECT, 0, 1);
  1250.      fl;
  1251.      cura = 0;
  1252.      goto same_prompt;
  1253.  
  1254.       case K_NEXT_LINE:
  1255.      if (numa < 0) goto nextmenu;
  1256.  
  1257.      cura++;
  1258.      goto same_prompt;
  1259.  
  1260.       case K_PREV_LINE:
  1261.      if (numa < 0) goto nextmenu;
  1262.  
  1263.      if (--cura < 0) cura = numa;
  1264.      goto same_prompt;
  1265.  
  1266.       case K_SELECT_SUBJECT:
  1267.      if (numa < 0) goto nextmenu;
  1268.  
  1269.      if (last_k_cmd != K_ARTICLE_ID && last_k_cmd != K_SELECT)
  1270.          toggle();
  1271.  
  1272.      while (firsta+cura > 0 &&
  1273.         (articles[firsta+cura]->flag & (A_SAME | A_ALMOST_SAME)))
  1274.          cura--;
  1275.  
  1276.      do {
  1277.          new_mark(last_attr);
  1278.          cura++;
  1279.          if (firsta+cura >= n_articles) break;
  1280.      } while (articles[firsta+cura]->flag & (A_SAME | A_ALMOST_SAME));
  1281.  
  1282.      goto same_prompt;
  1283.  
  1284.       case K_SELECT_RANGE:
  1285.      if (numa < 0) goto nextmenu;
  1286.  
  1287.      if (last_k_cmd == K_ARTICLE_ID || last_k_cmd == K_SELECT) {
  1288.          cura--;
  1289.          if (cura < 0) cura = numa;
  1290.      } else
  1291.          last_attr = (articles[firsta+cura]->attr & A_SELECT) ? 0 : A_SELECT;
  1292.  
  1293.       range_again:
  1294.  
  1295.      prompt("\1%select range\1 %c-", last_attr ? "S" : "Des", ident[cura]);
  1296.  
  1297.      k_cmd = get_k_cmd();
  1298.      if (k_cmd == K_SELECT_RANGE) {
  1299.          last_attr = last_attr ? 0 : A_SELECT;
  1300.          goto range_again;
  1301.      }
  1302.  
  1303.      if (k_cmd != K_ARTICLE_ID) goto Prompt;
  1304.  
  1305.      if (last_k_cmd != K_ARTICLE_ID && last_k_cmd != K_SELECT)
  1306.          new_mark(last_attr);
  1307.  
  1308.      if (article_id <= cura) {
  1309.          while (cura >= article_id) {
  1310.          new_mark(last_attr);
  1311.          cura--;
  1312.          }
  1313.          if (cura < 0) cura = 0;
  1314.      } else {
  1315.          while (cura <= article_id) {
  1316.          new_mark(last_attr);
  1317.          cura++;
  1318.          }
  1319.          if (cura > numa) cura = numa;
  1320.      }
  1321.      goto Prompt;
  1322.  
  1323.       case K_AUTO_SELECT:
  1324.      do_auto_select((regexp *)NULL, 1);
  1325.      goto same_prompt;
  1326.  
  1327.      case K_GOTO_MATCH:
  1328.      prompt("\1Select regexp\1 ");
  1329.      if ((fname = get_s(NONE, NONE, NONE, NULL_FCT)) == NULL)
  1330.          goto Prompt;
  1331.  
  1332.      if (*fname != NUL) {
  1333.          if (regular_expr) freeobj(regular_expr);
  1334.          if (case_fold_search) fold_string(fname);
  1335.          regular_expr = regcomp(fname);
  1336.      }
  1337.  
  1338.      if (regular_expr == NULL)
  1339.          msg("No previous expression");
  1340.      else
  1341.          do_auto_select(regular_expr, 2);
  1342.  
  1343.      goto Prompt;
  1344.  
  1345.       case K_NEXT_PAGE:
  1346.      if (nexta < n_articles) goto nextmenu;
  1347.      if (firsta == 0) goto same_prompt;
  1348.  
  1349.      nexta = 0;
  1350.      goto nextmenu;
  1351.  
  1352.       case K_PREV_PAGE:
  1353.      if (firsta == 0 && nexta == n_articles) goto same_prompt;
  1354.  
  1355.      nexta = (firsta > 0 ? firsta : n_articles) - maxa;
  1356.      if (nexta <= 1) nexta = 0;
  1357.      goto nextmenu;
  1358.  
  1359.       case K_FIRST_PAGE:
  1360.      if (firsta == 0) goto same_prompt;
  1361.  
  1362.      nexta = 0;
  1363.      goto nextmenu;
  1364.  
  1365.       case K_LAST_PAGE:
  1366.      if (nexta == n_articles) goto same_prompt;
  1367.  
  1368.      nexta = n_articles - maxa;
  1369.      if (nexta <= 1) nexta = 0;
  1370.      goto nextmenu;
  1371.  
  1372.       case K_PREVIEW:
  1373.      if (numa < 0) goto nextmenu;
  1374.  
  1375.       preview_other:
  1376.  
  1377.      prompt("\1Preview article\1");
  1378.      k_cmd = get_k_cmd();
  1379.  
  1380.      if (k_cmd != K_ARTICLE_ID) {
  1381.          if (k_cmd != K_PREVIEW)
  1382.          goto Prompt;
  1383.          article_id = cura;
  1384.      }
  1385.  
  1386.       auto_preview:
  1387.      temp = prompt_line;
  1388.  
  1389.       preview_next:
  1390.      cura = article_id;
  1391.      ah = articles[firsta+cura];
  1392.  
  1393.      no_raw();
  1394.      ah->attr = 0;
  1395.      menu_cmd = more(ah, MM_PREVIEW, prompt_line);
  1396.      if (menu_cmd == MC_MENU) {
  1397.          next_cura = cura;
  1398.          if (prompt_line < 0) goto redraw;
  1399.          mark();
  1400.          prompt_line = temp;
  1401.          goto build_prompt;
  1402.      }
  1403.  
  1404.      if (preview_mark_read && ah->attr == 0) ah->attr = A_READ;
  1405.      if (prompt_line >= 0)
  1406.          mark();
  1407.      next_cura = ++cura;
  1408.  
  1409.      switch (menu_cmd) {
  1410.  
  1411.       case MC_DO_KILL:
  1412.          if (!do_auto_kill()) break;
  1413.          elim_list[0] = firsta;
  1414.          elim_list[1] = firsta + cura;
  1415.          elim_articles(elim_list, 2);
  1416.          firsta = elim_list[0];
  1417.          next_cura = elim_list[1] - firsta;
  1418.          goto redraw;
  1419.  
  1420.       case MC_DO_SELECT:
  1421.          if (prompt_line >= 0) { /* not redrawn */
  1422.          do_auto_select((regexp *)NULL, 2);
  1423.          break;
  1424.          }
  1425.          numa = -1;
  1426.          do_auto_select((regexp *)NULL, 2);
  1427.          /* FALL THRU */
  1428.  
  1429.       case MC_QUIT:
  1430.          menu_return( ME_QUIT );
  1431.  
  1432.       case MC_REENTER_GROUP:
  1433.          menu_return( ME_REENTER_GROUP );
  1434.  
  1435.       case MC_NEXT:
  1436.       case MC_PREVIEW_NEXT:
  1437.          if (prompt_line < 0) {    /* redrawn screen ! */
  1438.          if (quit_preview(menu_cmd)) goto redraw;
  1439.          prompt_line = Lines;
  1440.          } else {
  1441. /*         if (ah->attr == A_LEAVE || ah->attr == A_LEAVE_NEXT) {
  1442.              cura--;
  1443.              mark();
  1444.              cura++;
  1445.          }
  1446. */         if (quit_preview(menu_cmd)) break;
  1447.          prompt_line = temp;
  1448.          }
  1449.          article_id = cura;
  1450.          goto preview_next;
  1451.  
  1452.       case MC_PREVIEW_OTHER:
  1453.          prompt_line = temp;
  1454.          raw();
  1455.          goto preview_other;
  1456.  
  1457.       default:
  1458.          if (prompt_line < 0) goto redraw;
  1459.          break;
  1460.      }
  1461.  
  1462.      prompt_line = temp;
  1463.      goto build_prompt;
  1464.  
  1465.       case K_LAYOUT:
  1466.      if (++fmt_linenum > 4) fmt_linenum = 0;
  1467.      goto redraw;
  1468.  
  1469.       default:
  1470.      msg("Command %d not supported", k_cmd);
  1471.      goto same_prompt;
  1472.      }
  1473.  
  1474.     no_raw();
  1475.  
  1476.     switch (show_articles()) {
  1477.  
  1478.      case MC_MENU:
  1479.     goto redraw;
  1480.  
  1481.      case MC_READGROUP:
  1482.     if (k_cmd == K_READ_GROUP_THEN_SAME || also_read_articles) goto redraw;
  1483.      case MC_NEXTGROUP:
  1484.     menu_cmd = ME_NEXT;
  1485.     break;
  1486.  
  1487.      case MC_REENTER_GROUP:
  1488.     menu_cmd = ME_REENTER_GROUP;
  1489.     break;
  1490.  
  1491.      case MC_QUIT:
  1492.     menu_cmd = ME_QUIT;
  1493.     break;
  1494.  
  1495.      default:
  1496.     sys_error("show_articles returned improper value");
  1497.     }
  1498.  
  1499.  menu_exit:
  1500.  
  1501.     firsta = o_firsta;
  1502.     in_menu_mode = o_mode;
  1503.     menu_level--;
  1504.  
  1505.     no_raw();
  1506.     return menu_cmd;
  1507. }
  1508.  
  1509.  
  1510. /*
  1511.  *    return article header for article on menu
  1512.  */
  1513.  
  1514. article_header *get_menu_article()
  1515. {
  1516.     register article_header *ah;
  1517.  
  1518.     fputs(" from article: ", stdout); fl;
  1519.  
  1520.     if (get_k_cmd() == K_ARTICLE_ID) {
  1521.     ah = articles[firsta + article_id];
  1522.     if (ah->a_group) init_group(ah->a_group);
  1523.     return ah;
  1524.     }
  1525.  
  1526.     return NULL;
  1527. }
  1528.  
  1529.  
  1530.  
  1531. /*
  1532.  *    read command from command line
  1533.  */
  1534.  
  1535. alt_command()
  1536. {
  1537.     int ok_val, macro_cmd;
  1538.     char *cmd, brkchars[10];
  1539.     extern key_type erase_key;
  1540.     extern int get_from_macro;
  1541.     extern int alt_completion();
  1542.  
  1543.     if (get_from_macro)
  1544.     ok_val = AC_UNCHANGED;
  1545.     else {
  1546.     prompt(":");
  1547.     ok_val = AC_PROMPT;
  1548.     }
  1549.  
  1550.  again:
  1551.  
  1552.     sprintf(brkchars, "?%c ", erase_key);
  1553.  
  1554.     cmd = get_s(NONE, NONE, brkchars, alt_completion);
  1555.     if (cmd == NULL ||
  1556.     *cmd == NUL || *cmd == SP || *cmd == erase_key)
  1557.     return ok_val;
  1558.  
  1559.     macro_cmd = get_from_macro;
  1560.  
  1561.     if (*cmd == '?') {
  1562.     display_file("help.extended", CLEAR_DISPLAY);
  1563.     ok_val = AC_REDRAW;
  1564.     goto new_prompt;
  1565.     }
  1566.  
  1567.     ok_val = parse_command(cmd, ok_val, (FILE *)NULL);
  1568.     if (ok_val != AC_REDRAW || !delay_redraw) return ok_val;
  1569.  
  1570.  new_prompt:
  1571.     if (macro_cmd) return ok_val;
  1572.  
  1573.     prompt_line = -1;
  1574.     printf("\n\r:");
  1575.     fl;
  1576.     goto again;
  1577. }
  1578.