*** ./LAST/data.h	Wed May  2 23:10:48 1990
--- data.h	Thu Apr 11 22:11:26 1991
***************
*** 152,163 ****
      char *	sender;		/* sender's name		*/
      char *	subject;	/* subject (w/o Re:)		*/
  
-     int16	replies;	/* no of Re:			*/
      int16	lines;		/* no of lines		*/
  
      int8	subj_length;	/* length of subject		*/
      int8	name_length;	/* length of sender		*/
  
      attr_type	attr;		/* attributes: 			*/
      attr_type	disp_attr;	/* currently displayed attr.	*/
      /* warning: notice relation between A_SELECT and A_AUTO_SELECT */
--- 152,165 ----
      char *	sender;		/* sender's name		*/
      char *	subject;	/* subject (w/o Re:)		*/
  
      int16	lines;		/* no of lines		*/
+     int8	replies;	/* no of Re:			*/
  
      int8	subj_length;	/* length of subject		*/
      int8	name_length;	/* length of sender		*/
  
+     int8	menu_line;	/* current line on menu		*/
+ 
      attr_type	attr;		/* attributes: 			*/
      attr_type	disp_attr;	/* currently displayed attr.	*/
      /* warning: notice relation between A_SELECT and A_AUTO_SELECT */
***************
*** 177,182 ****
--- 179,185 ----
  
  #	define A_SAME	    	FLAG(1)	/* same subject as prev. article */
  #	define A_ALMOST_SAME	FLAG(2)	/* A_SAME (match-limit)		*/
+ #	define A_ROOT_ART	FLAG(3)	/* root article in subject	*/
  #	define A_NEXT_SAME	FLAG(4)	/* next is same subject		*/
  #	define A_DIGEST   	FLAG(5)	/* digest sub article		*/
  #	define A_FULL_DIGEST	FLAG(6)	/* full digest			*/
***************
*** 185,190 ****
--- 188,195 ----
  #	define A_ST_FILED	FLAG(10) /* articles is saved		*/
  #	define A_ST_REPLY	FLAG(11) /* sent reply to article	*/
  #	define A_ST_FOLLOW	FLAG(12) /* sent followup to article	*/
+ #	define A_CLOSED		FLAG(13) /* subject closed on menu	*/
+ #	define A_HIDE		FLAG(14) /* hide subject on menu	*/
  
  } article_header;
  
*** ./LAST/doc/RELEASE_NOTES	Thu Apr 11 23:42:48 1991
--- doc/RELEASE_NOTES	Thu Apr 11 23:50:53 1991
***************
*** 1534,1540 ****
--- 1534,1553 ----
  From:	Michael Schwager <schwager@cs.uiuc.edu>
  Fixed:	Patch #14 [newsrc.c]
  
+ Prog:	nn
+ Title:	Current article can now be select by `.' when asked "... from article"
+ From:	farrell@batserver.cs.uq.oz.au (Friendless)
+ Fixed:	Patch #15 [menu.c]
+ 
+ 	Specifically, `K CR .'  would not kill the current article.
+ 	The fix allows `.' {select} everywhere an article id can be entered.
+ 
+ Prog:	nn
+ Title:	Should adjust number of unread articles so "1 of 20/1" is avoided.
+ From:	meulenbr@cst.philips.nl (Frans Meulenbroeks)
+ Fixed:	Patch #15 [menu.c]
  
+ 
  New features since initial 6.4.0 release
  ----------------------------------------
  
***************
*** 2064,2066 ****
--- 2077,2089 ----
  Added:	Patch #14 [answer.c config.h-dist]
  
  	Simply define NNTP_PATH_HOSTNAME as the path of the file.
+ 
+ Prog:	nn
+ Title:	Added "consolidated menus" (one menu line per subject)
+ From:	KFS on numerous requests
+ Added:	Patch #15 [data.h group.c keymap.c/.h menu.c sort.c variable.c nn.1]
+ 
+ Prog:	nn
+ Title:	Blank lines can now be added between menu lines via `menu-spacing'.
+ From:	KFS on request from salsbury@acsu.buffalo.edu (Patrick G. Salsbury)
+ Added:	Patch #15 [menu.c variable.c nn.1]
*** ./LAST/folder.c	Wed Feb  6 19:14:17 1991
--- folder.c	Thu Apr 11 22:27:04 1991
***************
*** 503,509 ****
  	goto move_back;
      }
  
!     sort_articles(0);
  
      printf("\rCompressing folder...\n\r"); fl;
  
--- 503,509 ----
  	goto move_back;
      }
  
!     sort_articles(-2);
  
      printf("\rCompressing folder...\n\r"); fl;
  
*** ./LAST/group.c	Thu Apr 11 22:05:34 1991
--- group.c	Thu Apr 11 22:27:04 1991
***************
*** 356,362 ****
  	if (access_mode & ACC_ALSO_READ_ARTICLES || first_art >= 0) continue;
  	if (menu_cmd != ME_NO_ARTICLES) gh->group_flag &= ~G_NEW;
  	if (gh->group_flag & G_UNSUBSCRIBED && !keep_unsub_long) continue;
! 	if (n) sort_articles(0);
  	n = 0;
  	update_rc(gh);
  	rc_merged_groups_hack = 1;
--- 356,362 ----
  	if (access_mode & ACC_ALSO_READ_ARTICLES || first_art >= 0) continue;
  	if (menu_cmd != ME_NO_ARTICLES) gh->group_flag &= ~G_NEW;
  	if (gh->group_flag & G_UNSUBSCRIBED && !keep_unsub_long) continue;
! 	if (n) sort_articles(-2);
  	n = 0;
  	update_rc(gh);
  	rc_merged_groups_hack = 1;
***************
*** 448,453 ****
--- 448,454 ----
      extern group_header *jump_to_group;
      article_number o_cur_first = -1;
      group_header *read_mode_group;
+     extern int bypass_consolidation;
  
  #define goto_return( cmd ) \
      { menu_cmd = cmd; goto goto_exit; }
***************
*** 848,853 ****
--- 849,855 ----
  	if (ah == NULL) goto_return(ME_NO_REDRAW);
  	strncpy(mask, (access_mode & ACC_ON_SUBJECT) ? ah->subject : ah->sender, GET_S_BUFFER);
  	mask[GET_S_BUFFER-1] = NUL;
+ 	bypass_consolidation = 1;
      }
  
      if (*mask) {
***************
*** 860,865 ****
--- 862,868 ----
      m_endinput();
      if (o_cur_first < 0) o_cur_first = gh->current_first;
      menu_cmd = group_menu(gh, first, access_mode, mask, menu);
+     bypass_consolidation = 0;	/* in case no articles were found */
      release_memory(&mem_marker);
  
   goto_exit:
*** ./LAST/keymap.c	Wed Nov  7 15:54:33 1990
--- keymap.c	Thu Apr 11 22:11:28 1991
***************
*** 330,337 ****
  /* %   */		K_PREVIEW,
  /* &   */	K_UNBOUND,
  /* '   */	K_UNBOUND,
! /* (   */	K_UNBOUND,
! /* )   */	K_UNBOUND,
  /* *   */		K_SELECT_SUBJECT,
  /* +   */		K_AUTO_SELECT,
  /* ,   */		K_NEXT_LINE,
--- 330,337 ----
  /* %   */		K_PREVIEW,
  /* &   */	K_UNBOUND,
  /* '   */	K_UNBOUND,
! /* (   */		K_OPEN_SUBJECT,
! /* )   */		K_CLOSE_SUBJECT,
  /* *   */		K_SELECT_SUBJECT,
  /* +   */		K_AUTO_SELECT,
  /* ,   */		K_NEXT_LINE,
***************
*** 453,458 ****
--- 453,459 ----
      "back-group",		K_BACK_GROUP,		0,
  
      "cancel",			K_CANCEL,		0,
+     "close-subject",		K_CLOSE_SUBJECT,	K_ONLY_MENU,
      "command",			K_EXTENDED_CMD,		0,
      "compress",			K_COMPRESS,		K_ONLY_MORE,
      "continue",			K_CONTINUE,		0,
***************
*** 490,495 ****
--- 491,497 ----
      "next-subject",		K_NEXT_SUBJECT,		K_ONLY_MORE,
      "nil",			K_UNBOUND,		0,
  
+     "open-subject",		K_OPEN_SUBJECT,		K_ONLY_MENU,
      "overview",			K_GROUP_OVERVIEW,	0,
  
      "page+1",			K_NEXT_PAGE,		0,
*** ./LAST/keymap.h	Mon Jul  9 17:59:56 1990
--- keymap.h	Thu Apr 11 22:11:28 1991
***************
*** 99,104 ****
--- 99,107 ----
  
  #define K_PREVIEW		0x004f /* preview article 		*/
  
+ #define K_OPEN_SUBJECT		0x0050 /* open subject on menu		*/
+ #define K_CLOSE_SUBJECT		0x0051 /* close subject on menu		*/
+ 
  #define	K_EQUAL_KEY		0x0070 /* map command special symbol	*/
  
  #define	K_MACRO			0x0100 /* call macro			*/
*** ./LAST/man/nn.1.A	Wed Nov  7 15:54:36 1990
--- man/nn.1.A	Thu Apr 11 23:53:53 1991
***************
*** 352,357 ****
--- 352,367 ----
  typical menu will thus only show each subject once, saving a lot of
  time in scanning the news articles.
  .LP
+ If \fIconsolidated menus\fP (see section below) are enabled, adjacent
+ articles sharing the same subject will be shown with a \fIsingle\fP
+ line on the menu corresponding to the \fIfirst\fP of the articles.
+ The number of articles with the same subject will be shown as a
+ braketed number in front of the subject, e.g. with layout 1:
+ .br
+ 	\fIx Name.........   123  [4] Subject..............\fP
+ .br
+ For further information see the section on consolidated menus below.
+ .LP
  \fBRelated variables\fP:
  collapse-subject, columns, confirm-entry, confirm-entry-limit,
  entry-report-limit, fsort, kill, layout, limit, lines, long-menu,
***************
*** 664,669 ****
--- 674,772 ----
  confirm-auto-quit, confirm-entry, confirm-junk-seen,
  marked-by-next-group, marked-by-read-return, marked-by-read-skip,
  retain-seen-status, select-on-sender.
+ .SH CONSOLIDATED MENUS
+ Normally, \fInn\fP will use one menu line for each article, so if
+ there are many articles with identical subjects, each menu page will
+ only contain a few different subjects.  To have each subject occur
+ only once on the menu, \fInn\fP can operate with consolidated menus by
+ setting the variable \fBconsolidated-menu\fP.
+ .LP
+ When consolidated menus are used, \fInn\fP operates with two kinds of
+ subjects: open and closed.
+ .LP
+ An \fIopen subject\fP is a subject which is shown in the traditional way
+ with one menu line for each article with the given subject.  In other
+ words, when consolidated menus are not used, all subjects are open (by
+ default).
+ .LP
+ A \fIclosed subject\fP is a multi-article subject which is presented
+ by a single menu line.  This line will be the normal menu line for the
+ first (oldest) article with the subject, but with the subject field
+ annotated with a bracketed number showing the number of articles with
+ that subject, e.g.
+ .sp 0.5v
+ 	a Kim F. Storm     12  [4] Future plans for nn
+ .br
+ 	b.Kim F. Storm     43  [3] More plans for nn
+ .sp 0.5v
+ In this example, there are four unread articles with subject `a' of
+ which the first is posted by me and has 12 lines.  The rest of the
+ articles are hidden, and will only be shown on request.  The `.'
+ marker on subject `b' shows that all three articles within that
+ subject have been read (or seen).
+ .LP
+ To select (or deselect) ALL the articles within a closed subject,
+ simply select the article shown on the menu; this will automatically
+ select (or deselect) the rest (see auto-select-closed).  When all the
+ unread articles within a closed subject are selected, the menu line
+ will be high-lighted.
+ .LP
+ If you want to view the individual articles in a subject (maybe to
+ select individual articles), you can open the subject with the
+ commands:
+ .TP
+ \fB(x\fP
+ Open subject \fIx\fP on menu.
+ .TP
+ \fB((\fP
+ Open current subject.
+ .LP
+ When you have completed viewing the opened subject, you can close it
+ again using the commands:
+ .TP
+ \fB)x\fP
+ Close subject \fIx\fP on menu (\fIx\fP is any article with the subject).
+ .TP
+ \fB))\fP
+ Close current subject.
+ .LP
+ In the basic layout of the menu line for a closed subject as shown
+ above, ALL articles in the closed subject are supposed to be either:
+ .TP
+ \fIunread\fP
+ The menu line is \fInot\fP high-lighted.
+ .TP
+ \fIselected\fP
+ Menu line is fully high-lighted (if all UNREAD are selected).
+ .TP
+ \fBread/seen\fP
+ There is a `.' (read attribute) following the article id.
+ .LP
+ If neither of these cases apply, i.e. there is a mixture of unread,
+ selected, and seen/read articles, the bracketed number will have one
+ of the following formats:
+ .TP
+ [U:T]
+ There are U unread articles of T total (U<T).
+ .TP
+ [S/T]
+ There are S selected articles of T total (S<U=T).
+ .TP
+ [S/U:T]
+ There are S selected of U unread of T total (S<U<T).
+ .LP
+ If there are any selected articles (S>0), the information between the
+ brackets will be high-lighted (to show that something is selected, but
+ not all the unread articles).
+ .LP
+ \fBNotice\fP:  Consolidated menus only work with the `subject' and
+ `lexical' sorting methods.
+ .LP
+ Variables related to consolidated menus are:
+ auto-select-closed,
+ consolidated-menu,
+ counter-delim-left, counter-delim-right,
+ save-closed-mode.
  .SH THE JUNK-ARTICLES AND LEAVE-NEXT COMMANDS
  The \fBJ\fP {\fBjunk-articles\fP} command is a very flexible command
  which can perform all sorts of attribute changes, either on individual
*** ./LAST/man/nn.1.C	Thu Apr 11 22:05:37 1991
--- man/nn.1.C	Thu Apr 11 23:53:53 1991
***************
*** 245,250 ****
--- 245,261 ----
  conditionally if the value is greater than zero and the number of
  unread articles in the current group does not exceed the given value.
  .TP
+ \fBauto-select-closed\fP \fImode\fP	(integer, default 1)
+ Normally, selecting a \fIclosed subject\fP (usually in consolidated
+ menu mode) will select (or deselect) all \fIunread\fP articles with
+ the given subject (or all articles if they are all read).  This
+ behaviour can be changed via the value of this variable as follows:
+ .nf
+ 0: select only the first article with the subject (shown on menu).
+ 1: select only the unread articles with the subject.
+ 2: select all available articles with the subject.
+ .fi
+ .TP
  \fBauto-select-subject\fP	(boolean, default false)
  When set, selecting an article from the menu using the article id
  (a-z), all articles on the menu with the same subject will
***************
*** 365,370 ****
--- 376,394 ----
  key.  (It will show the symbol <> to indicate that it is awaiting
  confirmation.)
  .TP
+ \fBconsolidated-menu\fP		(boolean, default false)
+ When set, \fInn\fP will automatically \fIclose\fP all multi-article
+ subjects on entry to a group, so that each subject only occur once on
+ the menu page.
+ .TP
+ \fBcounter-delim-left\fP	(string, default "[")
+ The delimiter string output to the left of the article counter in a
+ closed subject's menu line.
+ .TP
+ \fBcounter-delim-right\fP	(string, default "] ")
+ The delimiter string output to the right of the article counter in a
+ closed subject's menu line.
+ .TP
  \fBcross-filter-seq\fP		(boolean, default true)
  When set, cross posted articles will be presented in the first
  possible group, i.e. according to the current presentation sequence
***************
*** 804,809 ****
--- 828,844 ----
  the underline attribute).  This is typically used to give overlapping
  lines a different colour on terminals which have this capability.
  .TP
+ \fBmenu-spacing\fP \fImode\fP	(integer, default 0)
+ When \fImode\fP is a non-zero number as described below, \fInn\fP will
+ add blank lines between the lines on the menu to increase readability
+ at the cost of presenting fewer articles on each page.  The following
+ values of \fImode\fP are recognized:
+ .nf
+ 0: Don't add blank lines between menu lines.
+ 1: Add a blank line between articles with \fIdifferent\fP subjects.
+ 2: Add a blank line between \fIall\fP articles.
+ .fi
+ .TP
  \fBmessage-history\fP \fIN\fP	(integer, default 15)
  Specifies the maximum number, \fIN\fP, of older messages which can be
  recalled with the \fB^P\fP {\fBmessage\fP} command.
***************
*** 1110,1115 ****
--- 1145,1167 ----
  When set, \fInn\fP will try the specified number of \fItimes\fP to
  open an article before reporting that the article does not exist
  any more.  This may be necessary in some network environments.
+ .TP
+ \fBsave-closed-mode\fP \fImode\fP	(integer, default 13)
+ When saving an article in selection mode (i.e. by selecting it from
+ the menu), \fInn\fP will simply save the specified article if the
+ article's subject is \fIopen\fP.  When the selected menu entry is a
+ closed subject, the \fBsave-closed-mode\fP variable determines how
+ many articles among the closed articles should be saved:
+ .nf
+ 0: save root article (the one on the menu) only
+ 1: save selected articles within subject
+ 2: save unread (excl selected) articles within subject
+ 3: save selected+unread articles within subject
+ 4: save all articles within subject
+ .fi
+ If `10' is added to the above values, \fInn\fP will not save the
+ selected subject immediately; instead it will ask which articles
+ to save using the above value as the default answer.
  .TP
  \fBsave-counter\fP \fIformat\fP	(string, default "%d")
  This is the printf-format which \fInn\fP uses to create substitution
*** ./LAST/menu.c	Thu Jul 19 18:12:19 1990
--- menu.c	Thu Apr 11 22:11:32 1991
***************
*** 31,36 ****
--- 31,43 ----
  export int  show_purpose_mode = 1; /* 0: never, 1: new, 2: always */
  export int  read_ret_next_page = 0; /* Z returns to next page */
  
+ export int  consolidated_menu = 0; /* show only root articles */
+ export int  save_closed_mode = 13; /* ask how to save closed subj, dflt all */
+ export int  auto_select_closed = 1; /* select all in closed subject */
+ export int  menu_spacing = 0;	    /* number of screen lines per menu line */
+ export char *counter_delim_left  = "[";
+ export char *counter_delim_right = "] ";
+ 
  export int  auto_preview_mode = 0; /* preview rather than select */
  export int  preview_continuation = 12; /* what to do after preview */
  export int  preview_mark_read = 1; /* previewed articles are A_READ */
***************
*** 66,71 ****
--- 73,93 ----
  
  char attributes[30] = " .,+=#! **"; /* Corresponds to A_XXXX in data.h */
  
+ static int menu_length;		/* current no of line on menu */
+ static int menu_articles;	/* current no of articles on menu */
+ 
+ static struct menu_info {	/* info for each menu line */
+     int mi_cura;	/* cura corresponding to this menu line */
+     int mi_total;	/* total number of articles with this subject */
+     int mi_unread;	/* no of unread articles with this subject */
+     int mi_selected;	/* no of selected articles with this subject */
+     int mi_art_id;	/* article id (for mark()) */
+ } menu_info[INTERVAL1+INTERVAL2];
+ 
+ static struct menu_info *art_id_to_mi[INTERVAL1+INTERVAL2];
+ 
+ #define IS_VISIBLE(ah)	(((ah)->flag & (A_CLOSED | A_ROOT_ART)) != A_CLOSED)
+ 
  prt_replies(level)
  {
      int re;
***************
*** 107,130 ****
      return 11;
  }
  
  static mark()
  {
      register article_header *ah;
      int lno, lnum, lsubj, lname;
  
      ah = articles[firsta + cura];
      last_attr = ah->attr;
  
-     if (last_attr == ah->disp_attr) return;
      if (cura < 0 || cura > numa) return;
  
!     lno = firstl + cura;
!     if (ah->disp_attr == A_NOT_DISPLAYED) {
! 	gotoxy(0, lno);
! 	putchar(ident[cura]);
  	goto print_line;
      }
  
      /* A_AUTO_SELECT will not occur here! */
  
      if (!slow_mode)
--- 129,250 ----
      return 11;
  }
  
+ static article_number root_article(root)
+ register article_number root;
+ {
+     register article_header *ah = articles[root];
+ 
+     if (ah->flag & A_ROOT_ART)
+ 	return root;
+ 
+     if (ah->flag & A_CLOSED)	/* only root article is shown on menu */
+ 	return firsta + menu_info[ah->menu_line].mi_cura;
+ 
+     while (root > 0) {
+ 	if (articles[root]->flag & A_ROOT_ART) break;
+ 	root--;
+     }
+     return root;
+ }
+ 
+ static article_number next_root_article(root)
+ register article_number root;
+ {
+     while (++root < n_articles)
+ 	if (articles[root]->flag & A_ROOT_ART) break;
+     return root;
+ }
+ 
+ static set_root_if_closed()
+ {
+     if (articles[firsta + cura]->flag & A_CLOSED)
+ 	cura = root_article(firsta + cura) - firsta;
+ }
+ 
+ /*
+  *	count info for thread containing article #art (must be on menu!)
+  *	returns article number for next root article
+  */
+ 
+ static article_number thread_counters(art)
+ article_number art;
+ {
+     register struct menu_info *mi;
+     register article_number n;
+     int total, unread, selected, invisible;
+     
+     if (!(articles[art]->flag & A_CLOSED)) return art + 1;
+     
+     total = unread = selected = 0;
+     n = art = root_article(art);
+ 
+     while (n < n_articles) {
+ 	if (articles[n]->attr == 0) unread++;
+ 	else if (articles[n]->attr & A_SELECT) selected++; 
+ 	total++;
+ 	if (++n == n_articles) break;
+ 	if (articles[n]->flag & A_ROOT_ART) break;
+     }
+     unread += selected;
+ 
+     mi = menu_info + articles[art]->menu_line;
+     mi->mi_total = total;
+     mi->mi_unread = unread;
+     mi->mi_selected = selected;
+     return n;
+ }
+ 
+ static cursor_at_id()
+ {
+     gotoxy(0, firstl + articles[firsta + cura]->menu_line);
+     fl; /* place cursor at current article id */
+     save_xy();
+ }
+ 
  static mark()
  {
      register article_header *ah;
+     register struct menu_info *mi;
      int lno, lnum, lsubj, lname;
+     int only_counters;
+     char cbuf[80];
+     attr_type cattr;
  
      ah = articles[firsta + cura];
+     if (!IS_VISIBLE(ah)) return;
+     
      last_attr = ah->attr;
+     if (ah->disp_attr == A_NOT_DISPLAYED) {
+ 	mi = &menu_info[ah->menu_line];
+ 	lno = firstl + ah->menu_line;
+ 	gotoxy(0, lno);
+ 	putchar(ident[mi->mi_art_id]);
+ 	only_counters = 0;
+ 	goto print_line;
+     }
  
      if (cura < 0 || cura > numa) return;
  
!     lno = firstl + ah->menu_line;
! 
!     if (ah->flag & A_CLOSED) {
! 	struct menu_info old;
! 	mi = &menu_info[ah->menu_line];
! 	old = *mi;
! 	thread_counters(firsta + cura);
! 	if (old.mi_total == mi->mi_total &&
! 	    old.mi_selected == mi->mi_selected &&
! 	    old.mi_unread == mi->mi_unread) return;
! 
! 	if (old.mi_total == old.mi_selected)
! 	    only_counters = mi->mi_total == mi->mi_selected;
! 	else
! 	    only_counters = mi->mi_total != mi->mi_selected;
  	goto print_line;
      }
  
+     if (last_attr == ah->disp_attr) return;
+ 
      /* A_AUTO_SELECT will not occur here! */
  
      if (!slow_mode)
***************
*** 150,163 ****
         4	id   subject  (or as 1 if short subject)
       */
  
      if (fmt_linenum > 4) fmt_linenum = 1;
  
!     if (!slow_mode && (ah->attr & A_SELECT)) {
  	if (so_gotoxy(1, lno, 1) == 0)
  	    putchar(attributes[A_SELECT]);
      } else {
      	gotoxy(1, lno);
! 	putchar(attributes[ah->attr]);
      }
  
      if (ah->lines <    10) lnum = 1; else
--- 270,307 ----
         4	id   subject  (or as 1 if short subject)
       */
  
+     cattr = ah->attr;
+     cbuf[0] = NUL;
+     if (ah->flag & A_CLOSED) {
+ 	char sel[10], unr[10];
+ 
+ 	if (mi->mi_unread == 0)
+ 	    cattr = A_READ;
+ 	else if (mi->mi_total == mi->mi_selected)
+ 	    cattr = A_SELECT;
+ 	else if (auto_select_closed == 1 && mi->mi_unread == mi->mi_selected)
+ 	    cattr = A_SELECT;
+ 	else if (mi->mi_selected)
+ 	    cattr = A_KILL;	/* pseudo flag -> highlight cbuf */
+ 	else
+ 	    cattr = 0;
+ 
+ 	sel[0] = unr[0] = NUL;
+ 	if (mi->mi_selected && mi->mi_selected < mi->mi_unread)
+ 	    sprintf(sel, "%d/", mi->mi_selected);
+ 	if (mi->mi_unread && mi->mi_unread < mi->mi_total)
+ 	    sprintf(unr, "%d:", mi->mi_unread);
+ 	sprintf(cbuf, "%s%s%d", sel, unr, mi->mi_total);
+     }
+ 
      if (fmt_linenum > 4) fmt_linenum = 1;
  
!     if (!slow_mode && (cattr & A_SELECT)) {
  	if (so_gotoxy(1, lno, 1) == 0)
  	    putchar(attributes[A_SELECT]);
      } else {
      	gotoxy(1, lno);
! 	putchar(cattr == A_KILL ? ' ' : attributes[cattr]);
      }
  
      if (ah->lines <    10) lnum = 1; else
***************
*** 205,210 ****
--- 349,364 ----
  	break;
      }
  
+  print_subj:
+     if (cbuf[0]) {
+ 	so_printf("%s", counter_delim_left);
+ 	if (cattr == A_KILL) so_gotoxy(-1, -1, 0);
+ 	so_printf("%s", cbuf);
+ 	if (cattr == A_KILL) so_end();
+ 	so_printf("%s", counter_delim_right);
+ 	lsubj -= strlen(cbuf) + strlen(counter_delim_left) + strlen(counter_delim_right);
+     }
+     
      if (!fmt_rptsubj && lno > firstl && ah->flag & A_SAME) {
  	if (ah->replies == 0 || prt_replies(ah->replies) == 0)
  	    so_printf("-");
***************
*** 227,232 ****
--- 381,387 ----
  	so_printf(ah->lines >= 0 ? " +%d" : " +?", ah->lines);
  
      so_end();
+     if (ah->flag & A_CLOSED) clrline();
  
   out:
      ah->disp_attr = last_attr;
***************
*** 280,288 ****
--- 435,448 ----
  	if (!kill_file_loaded && !init_kill()) return 0;
  
      o_cura = cura;
+     cura = -1;
  
      for (i = 0, ahp = articles; i < n_articles; i++, ahp++) {
  	ah = *ahp;
+ 	if (cura >= 0 && (ah->flag & A_ROOT_ART)) {
+ 	    mark();
+ 	    cura = -1;
+ 	}
  	if (re != NULL) {
  	    if (!regexec_cf(re, select_on_sender ? ah->sender : ah->subject)) continue;
  	} else
***************
*** 290,302 ****
  
  	count++;
  	if (ah->attr & A_SELECT) continue;
  	if (firsta <= i && i <= (firsta+numa)) {
  	    cura = i - firsta;
! 	    new_mark(A_SELECT);
! 	} else
! 	    ah->attr = A_SELECT;
      }
  
      if (count)
  	msg("Selected %d article%s", count, plural((long)count));
      else
--- 450,467 ----
  
  	count++;
  	if (ah->attr & A_SELECT) continue;
+ 	ah->attr = A_SELECT;
  	if (firsta <= i && i <= (firsta+numa)) {
  	    cura = i - firsta;
! 	    if ((ah->flag & A_CLOSED) == 0) {
! 		mark();
! 		cura = -1;
! 	    }
! 	}
      }
  
+     if (cura >= 0) mark();
+ 
      if (count)
  	msg("Selected %d article%s", count, plural((long)count));
      else
***************
*** 320,326 ****
       case 1:
  	return 0;
       case 2:
! 	return (articles[firsta+cura]->flag & (A_SAME | A_ALMOST_SAME)) == 0;
      }
      return 0;
  }
--- 485,491 ----
       case 1:
  	return 0;
       case 2:
! 	return articles[firsta+cura]->flag & A_ROOT_ART;
      }
      return 0;
  }
***************
*** 404,410 ****
  	     case MC_NEXTSUBJ:
  		ah->attr = A_READ;
  		for (next = cur+1; next < n_articles; next++) {
! 		    if (((ah = articles[next])->flag & (A_SAME | A_ALMOST_SAME)) == 0) break;
  		    ah->attr = A_READ;
  		}
  		for (; next < n_articles; next++) {
--- 569,575 ----
  	     case MC_NEXTSUBJ:
  		ah->attr = A_READ;
  		for (next = cur+1; next < n_articles; next++) {
! 		    if ((ah = articles[next])->flag & A_ROOT_ART) break;
  		    ah->attr = A_READ;
  		}
  		for (; next < n_articles; next++) {
***************
*** 416,422 ****
  		ah->attr = A_READ;
  		for (next = cur+1; next < n_articles; next++) {
  		    ah = articles[next];
! 		    if ((ah->flag & (A_SAME | A_ALMOST_SAME)) == 0) break;
  		    ah->attr = A_SELECT;
  		}
  		for (next = cur+1; next < n_articles; next++)
--- 581,587 ----
  		ah->attr = A_READ;
  		for (next = cur+1; next < n_articles; next++) {
  		    ah = articles[next];
! 		    if (ah->flag & A_ROOT_ART) break;
  		    ah->attr = A_SELECT;
  		}
  		for (next = cur+1; next < n_articles; next++)
***************
*** 494,499 ****
--- 659,665 ----
  
  static int article_id;
  static int cur_key;
+ static int is_k_select;	/* set when K_ARTICLE_ID was really K_SELECT */
  
  static int get_k_cmd_1()
  {
***************
*** 522,535 ****
  	goto loop;
      }
  
      if (map & K_ARTICLE_ID) {
  	article_id = map & ~K_ARTICLE_ID;
  	map = K_ARTICLE_ID;
  
! 	if (article_id < 0 || article_id > numa) {
  	    ding();
  	    goto loop;
  	}
      }
  
      if (any_message && map != K_LAST_MESSAGE) clrmsg(-1);
--- 688,708 ----
  	goto loop;
      }
  
+     is_k_select = 0;
+     if (map == K_SELECT) {
+ 	map = K_ARTICLE_ID;
+ 	article_id = cura;
+ 	is_k_select = 1;
+     } else
      if (map & K_ARTICLE_ID) {
  	article_id = map & ~K_ARTICLE_ID;
  	map = K_ARTICLE_ID;
  
! 	if (article_id < 0 || article_id > menu_articles) {
  	    ding();
  	    goto loop;
  	}
+ 	article_id = art_id_to_mi[article_id]->mi_cura;
      }
  
      if (any_message && map != K_LAST_MESSAGE) clrmsg(-1);
***************
*** 575,591 ****
  int update;
  {
      int any;
  
      if (new == old) return 0;
      if (new == A_KILL) update = 0;
  
      any = 0;
      while (first < last) {
! 	if (old == A_KILL || articles[first]->attr == old) {
! 	    articles[first]->attr = new;
! 	    if (update) {
! 		cura = first-firsta;
! 		mark();
  	    }
  	    any = 1;
  	}
--- 748,776 ----
  int update;
  {
      int any;
+     register article_header *ah;
+     article_number ocura = cura;
  
      if (new == old) return 0;
      if (new == A_KILL) update = 0;
  
      any = 0;
+     cura = -1;
      while (first < last) {
! 	ah = articles[first];
! 	if (cura >= 0 && ah->flag & A_ROOT_ART) {
! 	    set_root_if_closed();
! 	    mark();
! 	    cura = -1;
! 	}
! 	if (old == A_KILL || ah->attr == old) {
! 	    ah->attr = new;
! 	    if (update && first >= firsta && first < nexta) {
! 		cura = first - firsta;
! 		if ((ah->flag & A_CLOSED) == 0) {
! 		    mark();
! 		    cura = -1;
! 		}
  	    }
  	    any = 1;
  	}
***************
*** 592,600 ****
--- 777,798 ----
  
  	first++;
      }
+ 
+     if (cura >= 0) {
+ 	set_root_if_closed();
+ 	mark();
+     }
+     cura = update ? last - firsta : ocura;
      return any;
  }
  
+ static repl_attr_subject(old, new, update)
+ attr_type old, new;
+ int update;
+ {
+     return repl_attr(root_article(firsta+cura), next_root_article(firsta+cura),
+ 		     old, new, update);
+ }
  
  static repl_attr_all(old, new, update)
  attr_type old, new;
***************
*** 631,636 ****
--- 829,878 ----
      }
  }
  
+ /*
+  * bypass_consolidation may be set to temporarily overrule the global
+  * consolidated_menu variable:
+  *	1:	don't consolidate	(e.g. for G=...  )
+  *	2:	do consolidate
+  *	3:	use consolidated_mode
+  */
+ 
+ export int bypass_consolidation = 0;
+ static int cur_bypass = 0;	/* current bypass status (see below) */
+ 
+ static do_consolidation()
+ {
+     int consolidate;
+ 
+     switch (bypass_consolidation) {
+      case 0:
+ 	break;
+      case 1:
+ 	cur_bypass = -1;	/* no consolidation */
+ 	break;
+      case 2:
+ 	cur_bypass = 1;		/* force consolidation */
+ 	break;
+      case 3:
+ 	cur_bypass = 0;		/* reset bypass to use consolidated_menu */
+ 	break;
+     }
+     bypass_consolidation = 0;
+ 
+     if (cur_bypass)
+ 	consolidate = cur_bypass > 0;
+     else
+ 	consolidate = consolidated_menu;
+ 
+     if (consolidate)
+ 	for (nexta = 0; nexta < n_articles; nexta++)
+ 	    articles[nexta]->flag |= A_CLOSED;
+     else
+ 	for (nexta = 0; nexta < n_articles; nexta++)
+ 	    articles[nexta]->flag &= ~A_CLOSED;
+ 
+     return consolidate;
+ }
  
  menu(print_header)
  fct_type print_header;
***************
*** 637,642 ****
--- 879,886 ----
  {
      register 		k_cmd, cur_k_cmd;
      register		article_header *ah;
+     register struct menu_info *mi;
+     int			consolidate, o_bypass;
      int			last_k_cmd;
      int 		menu_cmd, temp;
      int 		save_selected;
***************
*** 670,675 ****
--- 914,925 ----
      menu_level++;
  
      if (menu_level == 1) {
+ 	if ((current_group->group_flag & G_COUNTED)
+ 	    && n_articles != current_group->unread_count) {
+ 	    add_unread(current_group, -1);
+ 	    current_group->unread_count = n_articles;
+ 	    add_unread(current_group, 0);
+ 	}
  	entry_check = conf_group_entry && n_articles > conf_entry_limit;
  	auto_read = auto_read_limit < 0 || n_articles <= auto_read_limit;
      } else {
***************
*** 691,698 ****
--- 941,954 ----
  	 case 0: break;
  	 case 1: if ((current_group->group_flag & G_NEW) == 0) break;
  	 case 2: get_purpose(purpose);
+ 	         if (purpose[0]) strcpy(delayed_msg, purpose);
  	}
  
+     o_bypass = cur_bypass;
+     cur_bypass = 0;
+     
+     consolidate = do_consolidation();
+ 
      firsta = 0;
      while (firsta < n_articles && articles[firsta]->attr == A_SEEN)
  	firsta++;
***************
*** 768,800 ****
      gotoxy(0, firstl);
      clrpage(firstl);
  
!     if (nexta > 0) {
! 	firsta = nexta;
!     } else
! 	if (purpose[0]) {
! 	    msg(purpose);
! 	}
! 
      firsta = nexta;
-     numa = Lines; /* for mark; is set correctly below */
      cura = 0;
  
      REDRAW_CHECK;
  
!     if (!s_keyboard)
! 	while (nexta < n_articles && cura < maxa) {
  	    REDRAW_CHECK;
  
! 	    articles[firsta+cura]->disp_attr = A_NOT_DISPLAYED;
  	    mark();
! 	    nexta++; cura++;
  	}
  
      fl;
      s_keyboard = 0;
  
!     prompt_line = firstl + cura;
!     if (!long_menu || cura < maxa) prompt_line++;
  
      numa = nexta - firsta - 1;
       if (numa < 0) prompt_line++;
--- 1024,1106 ----
      gotoxy(0, firstl);
      clrpage(firstl);
  
!     if (articles[nexta]->flag & A_CLOSED)
! 	nexta = root_article(nexta);
      firsta = nexta;
      cura = 0;
  
      REDRAW_CHECK;
  
!     menu_length = 0;
!     menu_articles = 0;
!     goto draw_menu;
!     
!  partial_redraw:
!     next_cura = cura;
!  partial_redraw_nc:
!     nexta = firsta + cura;
!     menu_length = articles[nexta]->menu_line;
!     menu_articles = menu_info[menu_length].mi_art_id;
!     gotoxy(0, firstl + menu_length);
!     clrpage(firstl + menu_length);
! 
!  draw_menu:
!     if (!s_keyboard) {
! 	int first_menu_line = menu_length;
! 
! 	mi = menu_info + menu_length;
! 	while (nexta < n_articles) {
  	    REDRAW_CHECK;
  
! 	    ah = articles[nexta];
! 	    if (ah->flag & A_HIDE) {
! 		ah->menu_line = menu_length;	/* just in case.... */
! 		continue;
! 	    }
! 	    if (menu_length > first_menu_line) {
! 		switch (menu_spacing) {
! 		 case 0:
! 		    break;
! 		 case 1:
! 		    if ((ah->flag & A_ROOT_ART) == 0) break;
! 		 case 2:
! 		    menu_length++;
! 		    mi++;
! 		    break;
! 		}
! 		if (menu_length >= maxa) break;
! 	    }
! 	    
! 	    ah->menu_line = menu_length;
! 	    art_id_to_mi[menu_articles] = mi;
! 	    mi->mi_art_id = menu_articles++;
! 	    
! 	    mi->mi_cura = cura = nexta - firsta;
! 	    if (ah->flag & A_CLOSED) {	/* skip rest of thread */
! 		nexta = thread_counters(nexta);
! 		if (nexta == firsta + cura + 1)
! 		    ah->flag &= ~A_CLOSED;
! 		else {
! 		    article_number tmpa = firsta + cura;
! 		    while (++tmpa < nexta)
! 			articles[tmpa]->menu_line = menu_length;
! 		}
! 	    } else
! 		nexta++;
! 	    ah->disp_attr = A_NOT_DISPLAYED;
  	    mark();
! 	    if (++menu_length >= maxa) break;
! 	    mi++;
  	}
+     }
  
+     if (menu_length > maxa) menu_length = maxa;
+ 
      fl;
      s_keyboard = 0;
  
!     prompt_line = firstl + menu_length;
!     if (!long_menu || menu_length < maxa) prompt_line++;
  
      numa = nexta - firsta - 1;
       if (numa < 0) prompt_line++;
***************
*** 801,811 ****
--- 1107,1123 ----
  
       if (next_cura >= 0) {
  	 cura = next_cura;
+ 	 set_root_if_closed();
  	 next_cura = -1;
       } else {
  	 cura = 0;
  	 for (article_id = firsta; cura < numa; article_id++, cura++)
+ 	 {
  	     if ((articles[article_id]->attr & A_SELECT) == 0) break;	/*???*/
+ 	     if (!IS_VISIBLE(articles[article_id])) continue;
+ 	 }
+ 	 if (!IS_VISIBLE(articles[article_id]))
+ 	     cura = root_article(article_id) - firsta;
       }
  
    build_prompt:
***************
*** 824,835 ****
  
    same_prompt:
  
       if (cura < 0 || cura > numa) cura = 0;
  
       if (numa >= 0) {
! 	 gotoxy(0, firstl + cura);
! 	 fl; /* place cursor at current article id */
! 	 save_xy();
       }
  
       last_k_cmd = cur_k_cmd;
--- 1136,1148 ----
  
    same_prompt:
  
+     if (!IS_VISIBLE(articles[firsta+cura]))
+ 	cura = next_root_article(firsta + cura) - firsta;
+ 
       if (cura < 0 || cura > numa) cura = 0;
  
       if (numa >= 0) {
! 	 cursor_at_id();
       }
  
       last_k_cmd = cur_k_cmd;
***************
*** 874,879 ****
--- 1187,1193 ----
  	 goto same_prompt;
  
        case K_EXTENDED_CMD:
+ 	 temp = consolidated_menu;
  	 switch (alt_command()) {
  
  	  case AC_UNCHANGED:
***************
*** 887,894 ****
  
  	  case AC_REORDER:
  	     firsta = 0;
! 	     /* fall thru */
  	  case AC_REDRAW:
  	     goto redraw;
  
  	  case AC_KEYCMD:
--- 1201,1212 ----
  
  	  case AC_REORDER:
  	     firsta = 0;
! 	     consolidate = do_consolidation();
! 	     goto redraw;
! 
  	  case AC_REDRAW:
+ 	     if (temp != consolidated_menu)
+ 		 consolidate = do_consolidation();
  	     goto redraw;
  
  	  case AC_KEYCMD:
***************
*** 944,950 ****
  		 last_save = firsta + numa;
  	     } else
  	     if (k_cmd == K_AUTO_SELECT) {
! 		 save_selected = 2;
  		 cura = -firsta;
  		 article_id = 0;
  		 last_save = n_articles - 1;
--- 1262,1268 ----
  		 last_save = firsta + numa;
  	     } else
  	     if (k_cmd == K_AUTO_SELECT) {
! 		 save_selected = 1;
  		 cura = -firsta;
  		 article_id = 0;
  		 last_save = n_articles - 1;
***************
*** 953,964 ****
  		 cura = article_id;
  		 article_id += firsta;
  		 last_save = article_id;
  	     } else
  		 break;
  
  	     for ( ; article_id <= last_save ; article_id++, cura++) {
  		 ah = articles[article_id];
! 		 if (save_selected && (ah->attr & A_SELECT) == 0) continue;
  
  		 if (cur_k_cmd == K_CANCEL) {
  		     if (current_group->group_flag & G_FOLDER) {
--- 1271,1337 ----
  		 cura = article_id;
  		 article_id += firsta;
  		 last_save = article_id;
+ 		 if (articles[article_id]->flag & A_CLOSED) {
+ 		     int n = save_closed_mode % 10;
+ 		     int c;
+ 		     
+ 		     if (article_id == n_articles - 1) 
+ 			 goto save_it;
+ 		     if (articles[article_id + 1]->flag & A_ROOT_ART)
+ 			 goto save_it;
+ 
+ 		     if (save_closed_mode >= 10) {
+ 			 prompt("\1%s thread\1 (r)oot (s)elected (u)nread (b)oth (a)ll  (%c)",
+ 				savemode, "rsuba"[n]);
+ 			 switch (get_c()) {
+ 			  case 'r':	n = 0; break;
+ 			  case 's':	n = 1; break;
+ 			  case 'u':	n = 2; break;
+ 			  case 'b':	n = 3; break;
+ 			  case 'a':	n = 4; break;
+ 			  case SP:
+ 			  case CR:
+ 			  case NL:	break;
+ 			  default:	ding(); goto Prompt;
+ 			 }
+ 		     }
+ 		     switch (n) {
+ 		      case 0:		/* save root only */
+ 			 break;
+ 		      case 1:		/* save all selected */
+ 			 save_selected = 1;
+ 			 break;
+ 		      case 2:		/* save all unread */
+ 			 save_selected = 2;
+ 			 break;
+ 		      case 3:		/* save all selected + unread */
+ 			 save_selected = 3;
+ 			 break;
+ 		      case 4: 		/* save all articles */
+ 			 break;
+ 		     }
+ 		     if (n) last_save = next_root_article(article_id) - 1;
+ 		     save_selected |= 8;	/* closed subject */
+ 		     temp1 = cura;
+ 		 }
  	     } else
  		 break;
  
+ 	  save_it:
  	     for ( ; article_id <= last_save ; article_id++, cura++) {
  		 ah = articles[article_id];
! 		 switch (save_selected & 0x3) {
! 		  case 0:
! 		     break;
! 		  case 3:
! 		     if (ah->attr == 0) break;
! 		  case 1:
! 		     if ((ah->attr & A_SELECT) == 0) continue;
! 		     break;
! 		  case 2:
! 		     if (ah->attr == 0) break;
! 		     continue;
! 		 }		     
  
  		 if (cur_k_cmd == K_CANCEL) {
  		     if (current_group->group_flag & G_FOLDER) {
***************
*** 999,1004 ****
--- 1372,1382 ----
  			 mark();
  		 }
  	     }
+ 	     if (save_selected & 8) {
+ 		 save_selected = 0;	/* select closed */
+ 		 cura = temp1;
+ 		 mark();
+ 	     }
  	 }
  
  	 if (save_selected) cura = 0;
***************
*** 1149,1159 ****
--- 1527,1540 ----
  	 /* FALL THRU */
  
        case K_GOTO_GROUP:
+ 	 temp1 = n_articles;
  
  	 switch (goto_group(k_cmd, (article_header *)NULL, (flag_type)0)) {
  
  	  case ME_REDRAW:
  	     firsta = 0;
+ 	     if (temp1 != n_articles && consolidate)
+ 		 consolidate = do_consolidation();
  	     goto redraw;
  
  	  case ME_NO_ARTICLES:
***************
*** 1173,1178 ****
--- 1554,1618 ----
  	     goto empty_menu_hack;
  	 }
  
+       case K_OPEN_SUBJECT:
+ 	 if (numa < 0) goto nextmenu;
+ 	 prompt("\1Open subject\1");
+ 
+ 	 k_cmd = get_k_cmd();
+ 	 if (k_cmd != K_ARTICLE_ID) {
+ 	     if (k_cmd != cur_k_cmd) goto Prompt;
+ 	     article_id = cura;
+ 	 }
+ 
+       open_subject:
+ 	 ah = articles[firsta+article_id];
+ 	 if (!(ah->flag & A_CLOSED) ||
+ 	     (ah->flag & (A_ROOT_ART | A_NEXT_SAME)) == A_ROOT_ART)
+ 	     goto Prompt;
+ 
+ 	 cura = article_id = root_article(firsta + article_id) - firsta;
+ 	 while (cura + firsta < n_articles) {
+ 	     ah = articles[firsta+cura];
+ 	     if (cura > article_id && ah->flag & A_ROOT_ART)
+ 		 break;
+ 	     ah->flag &= ~A_CLOSED;
+ 	     cura++;
+ 	 }
+ 	 cura = article_id;
+ 	 goto partial_redraw;
+ 	 
+       case K_CLOSE_SUBJECT:
+ 	 if (numa < 0) goto nextmenu;
+ 	 prompt("\1Close subject\1");
+ 
+ 	 k_cmd = get_k_cmd();
+ 	 if (k_cmd != K_ARTICLE_ID) {
+ 	     if (k_cmd != cur_k_cmd) goto Prompt;
+ 	     article_id = cura;
+ 	 }
+ 
+ 	 ah = articles[firsta+article_id];
+ 	 if ((ah->flag & A_CLOSED) ||
+ 	     (ah->flag & (A_ROOT_ART | A_NEXT_SAME)) == A_ROOT_ART) {
+ 	     cura = next_root_article(firsta + cura) - firsta;
+ 	     goto Prompt;
+ 	 }
+ 
+ 	 cura = article_id = root_article(firsta + article_id) - firsta;
+ 	 while (cura + firsta < n_articles) {
+ 	     ah = articles[firsta+cura];
+ 	     if (cura > article_id && ah->flag & A_ROOT_ART)
+ 		 break;
+ 	     ah->flag |= A_CLOSED;
+ 	     cura++;
+ 	 }
+ 	 cura = article_id;
+ 	 next_cura = next_root_article(firsta + cura) - firsta;
+ 	 if (cura >= 0) goto partial_redraw_nc;
+ 	 articles[cura + firsta]->menu_line = articles[firsta]->menu_line;
+ 	 firsta += cura;
+ 	 goto redraw;
+ 
        case K_LEAVE_NEXT:
        case K_JUNK_ARTICLES:
  	 junk_prompt = cur_k_cmd == K_JUNK_ARTICLES ? 1 : 5;
***************
*** 1207,1213 ****
  
  	  junk_another:
  	     if (cura < 0 || cura > numa) cura = 0;
! 	     gotoxy(0, firstl + cura); fl;
  
  	     switch (get_k_cmd()) {
  	      case K_JUNK_ARTICLES:
--- 1647,1653 ----
  
  	  junk_another:
  	     if (cura < 0 || cura > numa) cura = 0;
! 	     cursor_at_id();
  
  	     switch (get_k_cmd()) {
  	      case K_JUNK_ARTICLES:
***************
*** 1216,1226 ****
  
  	      case K_ARTICLE_ID:
  		 cura = article_id;
- 	      case K_SELECT:
  		 if (junk_attr == A_KILL) junk_attr = A_READ;
! 		 articles[firsta + cura]->attr = junk_attr;
! 		 mark();
! 		 cura++;
  		 goto junk_another;
  
  	      case K_NEXT_LINE:
--- 1656,1669 ----
  
  	      case K_ARTICLE_ID:
  		 cura = article_id;
  		 if (junk_attr == A_KILL) junk_attr = A_READ;
! 		 if (auto_select_closed > 0 && articles[firsta + cura]->flag & A_CLOSED)
! 		     repl_attr_subject(A_KILL, junk_attr, 1);
! 		 else {
! 		     articles[firsta + cura]->attr = junk_attr;
! 		     mark();
! 		     cura++;
! 		 }
  		 goto junk_another;
  
  	      case K_NEXT_LINE:
***************
*** 1312,1325 ****
        case K_ARTICLE_ID:
  	 if (numa < 0) goto nextmenu;
  
! 	 if (auto_preview_mode) goto auto_preview;
  
  	 cura = article_id;
! 	 toggle();
! 	 if (auto_select_subject) goto select_subject;
! 	 mark();
! 	 cura++;
  
  	 goto same_prompt;
  
        case K_SELECT_INVERT:
--- 1755,1783 ----
        case K_ARTICLE_ID:
  	 if (numa < 0) goto nextmenu;
  
! 	 if (!is_k_select && auto_preview_mode) goto auto_preview;
  
  	 cura = article_id;
! 	 if (!auto_select_closed || !(articles[firsta+cura]->flag & A_CLOSED)) {
! 	     toggle();
! 	     if (!is_k_select && auto_select_subject) goto select_subject;
! 	     mark();
! 	     cura++;
! 	     goto same_prompt;
! 	 }
! 	 
! 	 if (auto_select_closed < 0) {
! 	     article_id = cura;
! 	     goto open_subject;
! 	 }
  
+ 	 mi = menu_info + articles[firsta+cura]->menu_line;
+ 	 if (mi->mi_unread == 0)
+ 	     repl_attr_subject(A_KILL, A_SELECT, 1);
+ 	 else if (mi->mi_selected == mi->mi_unread)
+ 	     repl_attr_subject(auto_select_closed == 2 ? A_KILL : A_SELECT, 0, 1);
+ 	 else
+ 	     repl_attr_subject(auto_select_closed == 2 ? A_KILL : 0, A_SELECT, 1);
  	 goto same_prompt;
  
        case K_SELECT_INVERT:
***************
*** 1330,1336 ****
  	 no_raw();	/* for x-on/x-off */
  	 for (cura = 0; cura <= numa; cura++) {
  	     toggle();
! 	     mark();
  	 }
  	 fl;
  
--- 1788,1796 ----
  	 no_raw();	/* for x-on/x-off */
  	 for (cura = 0; cura <= numa; cura++) {
  	     toggle();
! 	 }
! 	 for (cura = 0; cura <= numa; cura++) {
! 	     if (IS_VISIBLE(articles[firsta+cura])) mark();
  	 }
  	 fl;
  
***************
*** 1340,1354 ****
  	 cura = temp;
  	 goto same_prompt;
  
- 
-       case K_SELECT:
- 	 if (numa < 0) goto nextmenu;
- 
- 	 toggle();
- 	 mark();
- 	 cura++;
- 	 goto same_prompt;
- 
        case K_UNSELECT_ALL:
  	 if (last_k_cmd == K_UNSELECT_ALL)
  	     repl_attr_all(A_SELECT, 0, 1);
--- 1800,1805 ----
***************
*** 1368,1398 ****
  	 if (numa < 0) goto nextmenu;
  
  	 if (--cura < 0) cura = numa;
  	 goto same_prompt;
  
        case K_SELECT_SUBJECT:
  	 if (numa < 0) goto nextmenu;
  
! 	 if (last_k_cmd != K_ARTICLE_ID && last_k_cmd != K_SELECT)
  	     toggle();
  
        select_subject:
! 	 while (firsta+cura > 0 &&
! 		(articles[firsta+cura]->flag & (A_SAME | A_ALMOST_SAME)))
! 	     cura--;
! 
! 	 do {
! 	     new_mark(last_attr);
! 	     cura++;
! 	     if (firsta+cura >= n_articles) break;
! 	 } while (articles[firsta+cura]->flag & (A_SAME | A_ALMOST_SAME));
! 
  	 goto same_prompt;
  
        case K_SELECT_RANGE:
  	 if (numa < 0) goto nextmenu;
  
! 	 if (last_k_cmd == K_ARTICLE_ID || last_k_cmd == K_SELECT) {
  	     cura--;
  	     if (cura < 0) cura = numa;
  	 } else
--- 1819,1841 ----
  	 if (numa < 0) goto nextmenu;
  
  	 if (--cura < 0) cura = numa;
+ 	 set_root_if_closed();
  	 goto same_prompt;
  
        case K_SELECT_SUBJECT:
  	 if (numa < 0) goto nextmenu;
  
! 	 if (last_k_cmd != K_ARTICLE_ID)
  	     toggle();
  
        select_subject:
! 	 repl_attr_subject(A_KILL, last_attr, 1);
  	 goto same_prompt;
  
        case K_SELECT_RANGE:
  	 if (numa < 0) goto nextmenu;
  
! 	 if (last_k_cmd == K_ARTICLE_ID) {
  	     cura--;
  	     if (cura < 0) cura = numa;
  	 } else
***************
*** 1410,1431 ****
  
  	 if (k_cmd != K_ARTICLE_ID) goto Prompt;
  
! 	 if (last_k_cmd != K_ARTICLE_ID && last_k_cmd != K_SELECT)
! 	     new_mark(last_attr);
! 
! 	 if (article_id <= cura) {
! 	     while (cura >= article_id) {
! 		 new_mark(last_attr);
! 		 cura--;
! 	     }
! 	     if (cura < 0) cura = 0;
! 	 } else {
! 	     while (cura <= article_id) {
! 		 new_mark(last_attr);
! 		 cura++;
! 	     }
! 	     if (cura > numa) cura = numa;
  	 }
  	 goto Prompt;
  
        case K_AUTO_SELECT:
--- 1853,1865 ----
  
  	 if (k_cmd != K_ARTICLE_ID) goto Prompt;
  
! 	 if (article_id > cura) {
! 	     article_number tmp = cura;
! 	     cura  = article_id;
! 	     article_id = tmp;
  	 }
+ 
+ 	 repl_attr(article_id, cura, A_KILL, last_attr, 1);
  	 goto Prompt;
  
        case K_AUTO_SELECT:
***************
*** 1460,1467 ****
        case K_PREV_PAGE:
  	 if (firsta == 0 && nexta == n_articles) goto same_prompt;
  
! 	 nexta = (firsta > 0 ? firsta : n_articles) - maxa;
! 	 if (nexta <= 1) nexta = 0;
  	 goto nextmenu;
  
        case K_FIRST_PAGE:
--- 1894,1917 ----
        case K_PREV_PAGE:
  	 if (firsta == 0 && nexta == n_articles) goto same_prompt;
  
!       prevmenu:
! 	 nexta = (firsta > 0 ? firsta : n_articles);
! 	 
! 	 for (menu_length = maxa; menu_length > 0 && --nexta >= 0; ) {
! 	    if (!IS_VISIBLE(articles[nexta])) continue;
! 	    if (--menu_length > 0) {
! 		switch (menu_spacing) {
! 		 case 0:
! 		    break;
! 		 case 1:
! 		    if ((articles[nexta]->flag & A_ROOT_ART) == 0) break;
! 		 case 2:
! 		    --menu_length;
! 		    break;
! 		}
! 	    }
! 	 }
! 	 if (nexta < 0) nexta = 0;
  	 goto nextmenu;
  
        case K_FIRST_PAGE:
***************
*** 1472,1481 ****
  
        case K_LAST_PAGE:
  	 if (nexta == n_articles) goto same_prompt;
! 
! 	 nexta = n_articles - maxa;
! 	 if (nexta <= 1) nexta = 0;
! 	 goto nextmenu;
  
        case K_PREVIEW:
  	 if (numa < 0) goto nextmenu;
--- 1922,1929 ----
  
        case K_LAST_PAGE:
  	 if (nexta == n_articles) goto same_prompt;
! 	 firsta = 0;
! 	 goto prevmenu;
  
        case K_PREVIEW:
  	 if (numa < 0) goto nextmenu;
***************
*** 1616,1621 ****
--- 2064,2070 ----
  
   menu_exit:
  
+     cur_bypass = o_bypass;
      n_selected = o_selected;
      firsta = o_firsta;
      in_menu_mode = o_mode;
*** ./LAST/newsrc.c	Thu Apr 11 22:05:41 1991
--- newsrc.c	Thu Apr 11 22:27:03 1991
***************
*** 1010,1016 ****
  
  /*
   *	Update .newsrc for one group.
!  *	sort_articles(0) MUST HAVE BEEN CALLED BEFORE USE.
   */
  
  export int rc_merged_groups_hack = 0;
--- 1010,1016 ----
  
  /*
   *	Update .newsrc for one group.
!  *	sort_articles(-2) MUST HAVE BEEN CALLED BEFORE USE.
   */
  
  export int rc_merged_groups_hack = 0;
*** ./LAST/patchlevel.h	Thu Apr 11 22:05:41 1991
--- patchlevel.h	Thu Apr 11 22:11:32 1991
***************
*** 25,31 ****
   *	1990-11-07: Patch #12 (6.4.12) - LOW
   *	1991-02-06: Patch #13 (6.4.13) - MEDIUM
   *	1991-03-22: Patch #14 (6.4.14) - MEDIUM
   */
  
! #define PATCHLEVEL 14
  
--- 25,32 ----
   *	1990-11-07: Patch #12 (6.4.12) - LOW
   *	1991-02-06: Patch #13 (6.4.13) - MEDIUM
   *	1991-03-22: Patch #14 (6.4.14) - MEDIUM
+  *	1991-04-02: Patch #15 (6.4.15) - LOW
   */
  
! #define PATCHLEVEL 15
  
*** ./LAST/sort.c	Fri Oct  5 19:07:19 1990
--- sort.c	Thu Apr 11 23:05:09 1991
***************
*** 209,226 ****
      register long n;
      register flag_type same;
      fct_type cmp;
  
      for (n = n_articles; --n >= 0;)
! 	articles[n]->flag &= ~(A_SAME|A_ALMOST_SAME|A_NEXT_SAME);
  
!     if (n_articles <= 1) return;
  
      if (mode == -1) mode = sort_mode;
  
      switch (mode) {
       default:
       case 0:			/* arrival (no sort) */
  	cmp = order_arrival;
  	break;
       case 1:			/* date-subject-date */
       case 2:			/* subject-date */
--- 209,236 ----
      register long n;
      register flag_type same;
      fct_type cmp;
+     extern int bypass_consolidation;
  
+     if (n_articles <= 0) return;
+ 
      for (n = n_articles; --n >= 0;)
! 	articles[n]->flag &= ~(A_SAME|A_ALMOST_SAME|A_NEXT_SAME|A_ROOT_ART);
  
!     if (n_articles == 1) {
! 	articles[0]->flag |= A_ROOT_ART;
! 	return;
!     }
  
      if (mode == -1) mode = sort_mode;
  
      switch (mode) {
+      case -2:			/* restore original ordering for update */
+ 	cmp = order_arrival;
+ 	break;
       default:
       case 0:			/* arrival (no sort) */
  	cmp = order_arrival;
+ 	bypass_consolidation = 1;
  	break;
       case 1:			/* date-subject-date */
       case 2:			/* subject-date */
***************
*** 228,236 ****
--- 238,248 ----
  	break;
       case 3:			/* date only */
  	cmp = order_date;
+ 	bypass_consolidation = 1;
  	break;
       case 4:			/* sender-date */
  	cmp = order_from_date;
+ 	bypass_consolidation = 1;
  	break;
      }
  
***************
*** 237,242 ****
--- 249,255 ----
      quicksort(articles, n_articles, article_header *, cmp);
  
      articles[0]->root_t_stamp = articles[0]->t_stamp;
+     articles[0]->flag |= A_ROOT_ART;
      for (n = n_articles - 1, app = articles + 1; --n >= 0; app++) {
  	if (same = article_equal(app, app - 1)) {
  	    app[0]->root_t_stamp = app[-1]->root_t_stamp;
***************
*** 244,249 ****
--- 257,263 ----
  	    app[-1]->flag |= A_NEXT_SAME;
  	} else {
  	    app[0]->root_t_stamp = app[0]->t_stamp;
+ 	    app[0]->flag |= A_ROOT_ART;
  	}
      }
  
***************
*** 304,315 ****
      if (changed && n_articles > 0) {
  	srca = articles;
  	srca[0]->flag &= ~(A_SAME|A_ALMOST_SAME|A_NEXT_SAME);
  	for (n = n_articles - 1, srca++; --n >= 0; srca++) {
! 	    srca[0]->flag &= ~(A_SAME|A_ALMOST_SAME|A_NEXT_SAME);
  	    if (same = article_equal(srca, srca - 1)) {
  		srca[0]->flag |= same;
  		srca[-1]->flag |= A_NEXT_SAME;
! 	    }
  	}
      }
  
--- 318,331 ----
      if (changed && n_articles > 0) {
  	srca = articles;
  	srca[0]->flag &= ~(A_SAME|A_ALMOST_SAME|A_NEXT_SAME);
+ 	srca[0]->flag |= A_ROOT_ART;
  	for (n = n_articles - 1, srca++; --n >= 0; srca++) {
! 	    srca[0]->flag &= ~(A_SAME|A_ALMOST_SAME|A_NEXT_SAME|A_ROOT_ART);
  	    if (same = article_equal(srca, srca - 1)) {
  		srca[0]->flag |= same;
  		srca[-1]->flag |= A_NEXT_SAME;
! 	    } else
! 		srca[0]->flag |= A_ROOT_ART;
  	}
      }
  
*** ./LAST/variable.c	Wed Feb  6 19:14:23 1991
--- variable.c	Thu Apr 11 22:11:33 1991
***************
*** 32,37 ****
--- 32,38 ----
   *			Automatically expanded.  ("unset" set var to NULL).
   *	STR 3		Ordinary string, but cannot be "unset".
   *	STR 4		(Expanded) file name - cannot be unset.
+  *	STR 5		Ordinary string ("unset" set var to "").
   *
   *	CODES n		String initialized by list of key names.
   *			A maximum of 16 CODES variables (n = 0 - 15) exist.
***************
*** 63,68 ****
--- 64,71 ----
      *backup_folder_path,
      *bak_suffix,
      *bug_address,
+     *counter_delim_left,
+     *counter_delim_right,
      *decode_header_file,
      *default_distribution,
      *default_save_file,
***************
*** 119,124 ****
--- 122,128 ----
      conf_dont_sleep,
      conf_group_entry,
      conf_junk_seen,
+     consolidated_menu,
      delay_redraw,
      dflt_kill_select,
      do_kill_handling,
***************
*** 186,191 ****
--- 190,196 ----
      also_read_articles,
      article_limit,
      auto_read_limit,
+     auto_select_closed,
      check_db_update,
      conf_entry_limit,
      collapse_subject,
***************
*** 204,209 ****
--- 209,215 ----
      mark_next_group,
      mark_read_return,
      mark_read_skip,
+     menu_spacing,
      message_history,
      min_pv_window,
      multi_key_guard_time,
***************
*** 218,223 ****
--- 224,230 ----
      re_layout_more,
      response_check_pause,
      retry_on_error,
+     save_closed_mode,
      save_counter_offset,
      scroll_last_lines,
      show_purpose_mode,
***************
*** 291,296 ****
--- 298,304 ----
      "auto-junk-seen",		BOOL 0,		(char **)&auto_junk_seen,
      "auto-preview-mode",	BOOL 0,		(char **)&auto_preview_mode,
      "auto-read-mode-limit",	INT 0,		(char **)&auto_read_limit,
+     "auto-select-closed",	INT 0,		(char **)&auto_select_closed,
      "auto-select-subject",	BOOL 0,		(char **)&auto_select_subject,
      "backup",			BOOL 0,		(char **)&keep_rc_backup,
      "backup-folder-path",	STR 4,		(char **)&backup_folder_path,
***************
*** 311,316 ****
--- 319,327 ----
      "confirm-entry-limit",	INT 0,		(char **)&conf_entry_limit,
      "confirm-junk-seen", 	BOOL 0,		(char **)&conf_junk_seen,
      "confirm-messages",		BOOL 0,		(char **)&conf_dont_sleep,
+     "consolidated-menu",	BOOL 1,		(char **)&consolidated_menu,
+     "counter-delim-left",	STR 5,		(char **)&counter_delim_left,
+     "counter-delim-right",	STR 5,		(char **)&counter_delim_right,
      "cross-filter-seq",		BOOL 0,		(char **)&seq_cross_filtering,
      "cross-post",		BOOL 0,		(char **)&also_cross_postings,
      "data-bits",		INT 0,		(char **)&data_bits,
***************
*** 375,380 ****
--- 386,392 ----
      "marked-by-next-group",	INT 0,		(char **)&mark_next_group,
      "marked-by-read-return",	INT 0,		(char **)&mark_read_return,
      "marked-by-read-skip",	INT 0,		(char **)&mark_read_skip,
+     "menu-spacing",		INT 1,		(char **)&menu_spacing,
      "message-history",		INT 0,		(char **)&message_history,
      "min-window",		INT 1,		(char **)&min_pv_window,
      "mmdf-format",		BOOL 0,		(char **)&use_mmdf_folders,
***************
*** 419,424 ****
--- 431,437 ----
      "response-default-answer",	STR 0,		(char **)&response_dflt_answer,
      "retain-seen-status", 	BOOL 0,		(char **)&retain_seen_status,
      "retry-on-error",		INT 0,		(char **)&retry_on_error,
+     "save-closed-mode",		INT 0,		(char **)&save_closed_mode,
      "save-counter",		STR 3,		(char **)&save_counter_format,
      "save-counter-offset",	INT 0,		(char **)&save_counter_offset,
      "save-header-lines",	STR 0,		(char **)&save_header_lines,
***************
*** 622,627 ****
--- 635,644 ----
  	    }
  	    STR_VAR = copy_str(val_string);
  	    break;
+ 	 case 5:
+ 	    STR_VAR = (on && val_string) ? copy_str(val_string) : "";
+ 	    break;
+ 
  	}
  	break;
