[news.software.nn] nn 6.4 - Official patch #16

storm@texas.dk (Kim F. Storm) (04/19/91)

	     This is an official patch to nn release 6.4
	     -------------------------------------------

			      PATCH #16

			    Priority: HIGH


Adding consolidated menus in patch #15 involved quite significant
changes to central parts of the menu handling logic, so some bugs were
to expected.  But it caused more serious bugs than I would have tought
possible given that I had tested it to various degrees on three
different platforms.  Most significantly was a bug that caused a core
dump when the last article in the group was touched - even when
consolidated menus were not used.

There were also a stupid bug in the way range selections were handled
which would cause it to select too few or completely wrong ranges of
articles.

There would be problems handling the case where no sorting is
performed on the articles to be presented on the menu - specifically
in the online manual, but also if `fsort' or `sort' were not set when
entering a folder or a group.

A nasty bug in the "nnmaster -k" logic has been found (not related to
patch #15), where nnmaster may potentially send a SIGTERM to process 1
(init) if nnmaster is found to be running on another host.  Usually,
this wouldn't cause problems since nnmaster is not supposed to run as
root, but you never know...

Some other problems not related to patch #15 are also fixed by this
patch:

- Nn will now offer to use a .newsrc.bak fil if .newsrc is missing or empty.

- Problems with nnadmin D)ata and V)alidate incorrectly reporting corruption
  for empty groups have been fixed.

- Reply in :rmail no longer makes an "In /usr/mail/.... you write" line.

- Superfluous "Distribution: world" header lines are eliminated.


A few additions has also been made in this patch:

I have received some complaints about the layout of the consolidated
menu - it is too messy, especially since the subjects are not aligned.
It is hard to do this perfectly, but there is a new variable
`counter-padding' which can be set to a minimum padding of all
subjects to start approximately at the same column (default is 5).

There is also a new boolean variable `consolidated-manual' which can
be set to present the online manual with a "one line per program"
selection menu.  (default off).  To make use of this feature, the dumb
"Name" entry on the online manual has been changed to show the
contents of the NAME section, i.e. the purpose of the program.

Hooks to allow mail aliases to be expanded are now now provided
through the `mail-alias-expander' variable.  To use this feature,
programs must be written to actually expand the aliases.  Such
programs may be included in future patches as they become available.


As usual, all changes are described in the updated RELEASE_NOTES file
(read that for more details about this patch).  Thanks to all who
reported bugs and provided fixes.

To apply this patch, use nn's :patch command, or run this command from
the shell in the root of the nn source tree:
	patch -p0 < this-article

Then run "make all" and "./inst u".

++Kim Storm

===================================================================

*** ./LAST/admin.c	Fri Apr 12 19:28:20 1991
--- admin.c	Tue Apr 16 19:57:52 1991
***************
*** 274,282 ****
  
      if (verbose) { printf("\r%s: ", gh->group_name); clrline(); }
  
!     if (init_group(gh) <= 0) {
!       if (verbose)
! 	printf("NO DIRECTORY (ok)");
  	return 1; /* no directory/ignored */
      }
  
--- 274,282 ----
  
      if (verbose) { printf("\r%s: ", gh->group_name); clrline(); }
  
!     /* init_group returns ok for gh == current_group before check on NO_DIR */
!     if ((gh->master_flag & M_NO_DIRECTORY) || init_group(gh) <= 0) {
! 	if (verbose) printf("NO DIRECTORY (ok)");
  	return 1; /* no directory/ignored */
      }
  
***************
*** 433,438 ****
--- 433,443 ----
  
      update_group(gh);
  
+     if (gh->index_write_offset == 0) {
+ 	printf("group %s is empty\n", gh->group_name);
+ 	return 1;
+     }
+     
      first_article = get_entry("First article",
  			      (long)gh->first_db_article,
  			      (long)gh->last_db_article);
*** ./LAST/answer.c	Fri Apr 12 19:28:21 1991
--- answer.c	Thu Apr 18 10:27:24 1991
***************
*** 33,38 ****
--- 33,39 ----
  export int  inews_pipe_input	= 1;
  export char *editor_program	= NULL;
  export char *spell_checker	= NULL;
+ export char *mail_alias_expander = NULL;
  
  export char *bug_address	= BUG_REPORT_ADDRESS;
  
***************
*** 363,368 ****
--- 364,370 ----
  	if (editor_program != NULL)
  	    aux_param_str(param, "EDITOR", editor_program);
  	aux_param_str(param, "SPELL_CHECKER", spell_checker);
+ 	aux_param_str(param, "ALIAS_EXPANDER", mail_alias_expander);
  	aux_param_str(param, "PAGER", pager);
  	aux_param_str(param, "MAILER", mailer_program);
  	aux_param_bool(param, "MAILER_PIPE", mailer_pipe_input);
***************
*** 578,584 ****
  	end_header(t, extra_mail_headers);
  
  	if (incl) {
! 	    fprintf(t, "In %s you write:\n", current_group->group_name);
  	    ed_line++;
  	}
      }
--- 580,589 ----
  	end_header(t, extra_mail_headers);
  
  	if (incl) {
! 	    if (current_group->group_flag & G_FOLDER)
! 		fprintf(t, "You write:\n");
! 	    else
! 		fprintf(t, "In %s you write:\n", current_group->group_name);
  	    ed_line++;
  	}
      }
***************
*** 622,629 ****
  
  	str = get_distr(news.ng_dist, distribution_follow);
  	if (str == NULL) goto close_t;
! 	fprintf(t, "Distribution: %s\n", str);
! 	ed_line++;
  
  	ref_line(t);
  
--- 627,636 ----
  
  	str = get_distr(news.ng_dist, distribution_follow);
  	if (str == NULL) goto close_t;
! 	if (strcmp(str, "world")) {
! 	    fprintf(t, "Distribution: %s\n", str);
! 	    ed_line++;
! 	}
  
  	ref_line(t);
  
***************
*** 861,867 ****
      int ngroups, i;
      char newsgroups[FILENAME*2];
  
!     init_term(1);
      visit_init_file(0, (char *)NULL);
      init_answer();
      current_group = NULL;
--- 868,874 ----
      int ngroups, i;
      char newsgroups[FILENAME*2];
  
!     init_term(0);
      visit_init_file(0, (char *)NULL);
      init_answer();
      current_group = NULL;
***************
*** 884,889 ****
--- 891,905 ----
  	post_to_groups = newsgroups;
      }
  
+     if (ngroups > 0 && post_no_edit && post_subject && post_distribution) {
+ 	if (!post_summary) post_summary = "";
+ 	if (!post_keywords) post_keywords = "";
+ 	post_menu();
+ 	goto no_dialogue;
+     }
+     
+     init_term(1);
+ 
      raw();
      clrdisp();
      prompt_line = 0;
***************
*** 890,895 ****
--- 906,913 ----
      if (post_menu() == 2) clrdisp();
      putchar(CR); putchar(NL);
      unset_raw();
+ 
+  no_dialogue:
      if (*delayed_msg)
  	printf("%s\n", delayed_msg);
  
***************
*** 1006,1012 ****
      if (!post_no_edit)
  	prompt("\1WAIT\1");
  
!     ed_line = 4;
  #ifdef NNTP
  #ifdef NNTP_MINI_INEWS_HEADER
      mini_inews_header(t);
--- 1024,1030 ----
      if (!post_no_edit)
  	prompt("\1WAIT\1");
  
!     ed_line = 3;
  #ifdef NNTP
  #ifdef NNTP_MINI_INEWS_HEADER
      mini_inews_header(t);
***************
*** 1013,1019 ****
  #endif
  #endif
      fprintf(t, "Newsgroups: %s\n", group_name);
!     fprintf(t, "Distribution: %s\n", distribution);
      fprintf(t, "Subject: %s\n", subject);
      if (*summary) {
  	fprintf(t, "Summary: %s\n", summary);
--- 1031,1040 ----
  #endif
  #endif
      fprintf(t, "Newsgroups: %s\n", group_name);
!     if (strcmp(distribution, "world")) {
! 	fprintf(t, "Distribution: %s\n", distribution);
! 	ed_line++;
!     }
      fprintf(t, "Subject: %s\n", subject);
      if (*summary) {
  	fprintf(t, "Summary: %s\n", summary);
*** ./LAST/articles.c	Tue Sep 18 12:44:50 1990
--- articles.c	Tue Apr 16 21:15:11 1991
***************
*** 328,334 ****
  		    n = db_hdr.dh_cross_postings;
  		    break;
  		}
! 		if (cpgh->group_flag & G_UNSUBSCRIBED) continue;
  
  		if (!seq_cross_filtering) break;
  		if (cpgh->preseq_index > 0 &&
--- 328,335 ----
  		    n = db_hdr.dh_cross_postings;
  		    break;
  		}
! 		if (!(flags & ACC_ALSO_UNSUB_GROUPS))
! 		    if (cpgh->group_flag & G_UNSUBSCRIBED) continue;
  
  		if (!seq_cross_filtering) break;
  		if (cpgh->preseq_index > 0 &&
***************
*** 467,472 ****
--- 468,475 ----
  
      if ((flags & ACC_DONT_SORT_ARTICLES) == 0)
  	sort_articles(-1);
+     else
+ 	no_sort_articles();
  
      return n_articles > 0 ? 1 : 0;
  
*** ./LAST/articles.h	Thu Jul 19 18:12:10 1990
--- articles.h	Tue Apr 16 21:15:12 1991
***************
*** 54,56 ****
--- 54,57 ----
  #define ACC_DO_KILL		FLAG(14) /* do auto-kill/select */
  #define	ACC_PARSE_VARIABLES	FLAG(15) /* kill, split, etc. */
  #define ACC_MERGED_NEWSRC	FLAG(16) /* merge orig and cur .newsrc */
+ #define ACC_ALSO_UNSUB_GROUPS	FLAG(17) /* kill x-posts based on unsub also */
*** ./LAST/aux.sh	Wed Feb  6 19:14:12 1991
--- aux.sh	Tue Apr 16 23:46:20 1991
***************
*** 27,32 ****
--- 27,33 ----
  #	MAILER		[mailer]
  #	MAILER_PIPE	[mailer-pipe-input]
  #	DFLT_ANSW	[response-default-answer]
+ #	ALIAS_EXPANDER	[mail-alias-expander]
  
  CC=""
  
***************
*** 199,204 ****
--- 200,206 ----
  	if [ -n "$COPY" ] ; then
  	  rm -f $COPY
  	fi
+ 	rm -f $WORK
  	exit 22 ;;
      esac
      ;;
***************
*** 339,344 ****
--- 341,349 ----
    case "$OPERATION" in
  
      reply|forward|mail)
+       if [ -n "${ALIAS_EXPANDER}" ] ; then
+ 	${ALIAS_EXPANDER} $FINAL
+       fi
        if ${MAILER_PIPE} ; then
          $MAILER < $FINAL
          x=$?
*** ./LAST/doc/RELEASE_NOTES	Fri Apr 12 19:29:27 1991
--- doc/RELEASE_NOTES	Thu Apr 18 10:50:44 1991
***************
*** 1547,1553 ****
--- 1547,1682 ----
  From:	meulenbr@cst.philips.nl (Frans Meulenbroeks)
  Fixed:	Patch #15 [menu.c]
  
+ Prog:	nnmaster
+ Title:	-k option would kill process `1' if master is running on another host.
+ From:	Peter Wemm <peter@cutmcvax.cs.curtin.edu.au>
+ Fixed:	Patch #16 [master.c]
+ 
+ Prog:	nn
+ Title:	dumps core after selecting/previewing/junking last article in group
+ From:	Steven.Grimm@ebay.sun.com (Steven Grimm) +fix
+ 	Toshinori Maeno <tmaeno@cc.titech.ac.jp> +fix
+ 	eggert@twinsun.com (Paul Eggert) +fix
+ 	... and many more ...
+ Fixed:	Patch #16 [menu.c]
+ 
+ Prog:	nn
+ Title:	Selecting range a-d only selects articles a-c.
+ From:	Tom Ivar Helbekkmo <tih@barsoom.nhh.no>
+ Fixed:	Patch #16 [menu.c]
+ 
+ Prog:	nn
+ Title:	Selecting range c-l on *second* menu page only selects wrong articles.
+ From:	dean%coplex@relay.eu.net (Dean Brooks)
+ Fixed:	Patch #16 [menu.c]
+ 
+ Prog:	nn
+ Title:	:man command gives an empty menu if consolidated-menu is on.
+ From:	Bill Irwin <bill@twg.bc.ca>
+ Fixed:	Patch #16 [folder.c sort.c]
+ 
+ Prog:	nn
+ Title:	:man command dumps core when entry is selected when cons-menu is off.
+ From:	Bill Irwin <bill@twg.bc.ca>
+ Fixed:	Patch #16 [folder.c sort.c]
+ 
+ Prog:	nn
+ Title:	Reading folders with fsort=off or groups with sort=off fails horribly.
+ From:	KFS
+ Fixed:	Patch #16 [articles.c folder.c group.c sort.c]
+ 
+ Prog:	nn
+ Title:	Selecting a closed subject in slow_mode still redraw the whole line.
+ From:	david@wyvern.cs.uow.edu.au (David E A Wilson)
+ 
+ Prog:	nn
+ Title:	A long menu line for a closed subject will sometimes wrap to next line.
+ From:	david@wyvern.cs.uow.edu.au (David E A Wilson)
+ Fixed:	Patch #16 [menu.c]
+ 
+ 	If found a "cookie" that wasn't counted - I hope that was it...
+ 
+ Prog:	nn
+ Title:	Subjects are not aligned on consolidated menus.
+ From:	mpp@uf.msc.umn.edu (Mike Pritchard)
+ Fixed:	Patch #16 [menu.c variable.c nn.1]
+ 
+ 	There is a new variable counter-padding (=5) to control this.
+ 
+ Prog:	regression testing
+ Title:	The time stamps in scripts and #nn in version prevent regression tests.
+ From:	eggert@twinsun.com (Paul Eggert)
+ Fixed:	Patch #16 [prefix.c global.c]  (removed both)
+ 
+ Prog:	nn, nnpost
+ Title:	A "Distribution: world" line should be omitted from header.
+ From:	tale@cs.rpi.edu (David C Lawrence)
+ Fixed:	Patch #16 [answer.c]
+ 
+ Prog:	nn
+ Title:	:rmail reply says: In /usr/mail/spool/wb8foz/mbox, ...
+ From:	David Lesher <wb8foz@mthvax.cs.miami.edu>
+ Fixed:	Patch #16 [answer.c]
+ 
+ Prog:	nnadmin
+ Title:	Dumping D)ata in an empty group reports corrupted database.
+ From:	david@cs.uow.edu.au (David E A Wilson)
+ Fixed:	Patch #16 [admin.c]
+ 
+ Prog:	nnadmin
+ Title:	V)alidate in G)roup menu for group with NO_DIR would report errors.
+ From:	KFS
+ Fixed:	Patch #16 [admin.c]
  
+ Prog:	nn
+ Title:	`unset fsort' doesn't work unless `set fsort' has been used.
+ From: 	root@speech.kth.se (Roger Lindell)
+ Fixed:	Pacth #16 [variable.c]
+ 
+ Prog:	nn
+ Title:	Some systems use // in network file names, but they cannot be entered.
+ From:	itkin@guinan.transact.com (Steven List)
+ Fixed:	Patch #16 [term.c variable.c nn.1]
+ 
+ 	There is a new `guard-double-slash' variable that can be set.
+ 
+ Prog:	nn, nngrab
+ Title:	nn -xXm would sometimes not eliminate cross-posted articles.
+ From:	eggert@twinsun.com (Paul Eggert)
+ Fixed:	Patch #16 [articles.c articles.h group.c sequence.c]
+ 
+ Prog:	nn
+ Title:	.newsrc.bak is removed if .newsrc doesn't exist (or is EMPTY).
+ From:	geoff@world.std.com <Geoff Collyer>
+ Fixed:	Patch #16 [newsrc.c]
+ 
+ Prog:	nn
+ Title:	h)old option did not remove temporary work file.
+ From:	KFS
+ Fixed:	Patch #16 [aux.sh]
+ 
+ Prog:	nn
+ Title:	G {goto-group} does not honour: nosplit, nosort, kill, a.o.
+ From:	jfsenior@unix1.tcd.ie (John Senior)
+ Fixed:	Patch #16 [group.c]
+ 
+ Prog:	nngrep
+ Title:	running nngrep without arguments may give memory allocation error.
+ From:	dean%coplex@relay.eu.net (Dean Brooks)
+ Fixed:	Patch #16 [newsrc.c]
+ 
+ Prog:	nncheck
+ Title:	nncheck -Q requires $TERM variable is set.
+ From:	allan@rind.cs.cornell.edu (James Allan)
+ 	pt%geovision@uunet.UU.NET (Paul Tomblin)
+ Fixed:	Patch #16 [term.c]
+ 
+ Prog:	nnpost
+ Title:	nnpost requires $TERM variable also when no input is required.
+ From:	rock@warp.Eng.Sun.COM (Bill Petro)
+ Fixed:	Patch #16 [answer.c term.c]
+ 
+ 
  New features since initial 6.4.0 release
  ----------------------------------------
  
***************
*** 2087,2089 ****
--- 2216,2246 ----
  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]
+ 
+ Prog:	nn
+ Title:	The online manual can now be consolidated too (one program per line)
+ From:	KFS
+ Added:	Patch #16 [format.awk folder.c variable.c nn.1]
+ 
+ 	There is a new boolean variable to set: consolidated-manual.
+ 
+ Prog:	nn, nngrab
+ Title:	The update intervals for the merge report in nngrab can now be changed.
+ From:	KFS
+ Added:	Patch #16 [variable.c nn.1]
+ 
+ 	There is a new integer variable `merge-report-rate'.
+ 
+ Prog:	nn
+ Title:	Hooks are provided to allow mail aliases to be expanded.
+ From:	KFS on numerous requests.
+ Added:	Patch #16 [aux.sh answer.c variable.c nn.1]
+ 
+ 	There is a new variable `mail-alias-expander' which may be set
+ 	to a command which will expand aliases in the file given to it
+ 	as its last argument.  No alias expanders are currently avaliable.
+ 
+ Prog:	nnpost
+ Title:	Simplified and documented unattended posting of articles.
+ From:	KFS
+ Added:	Patch #16 [answer.c nnpost.1]
*** ./LAST/folder.c	Fri Apr 12 19:29:28 1991
--- folder.c	Mon Apr 15 20:56:52 1991
***************
*** 23,28 ****
--- 23,29 ----
  export char *backup_folder_path = "BackupFolder~";
  export int  keep_backup_folder = 1;
  export int  convert_folder_mode = 0; /* ignore folder's format on rewrite */
+ export int  consolidated_manual = 0;
  
  import int fmt_linenum;
  import char *header_lines;
***************
*** 409,416 ****
  	if (n_articles > 1) {
  	    clrdisp();
  	    prompt_line = 2;
! 	    if (mode == 0 && !dont_sort_folders) sort_articles(-1);
! 	}
  
  	cc_save = cancel_count;
  	cancel_count = 0;
--- 410,434 ----
  	if (n_articles > 1) {
  	    clrdisp();
  	    prompt_line = 2;
! 	    if (mode == 0 && !dont_sort_folders)
! 		sort_articles(-1);
! 	    else if (mode == 1) {
! 		extern int bypass_consolidation;
! 		article_number n;
! 		for (n = 0; n < n_articles; n++) {
! 		    ah = articles[n];
! 		    if (n == 0)
! 			ah->flag |= A_ROOT_ART;
! 		    else if (strcmp(ah->sender, articles[n-1]->sender) == 0)
! 			articles[n-1]->flag |= A_NEXT_SAME;
! 		    else
! 			ah->flag |= A_ROOT_ART;
! 		}
! 		bypass_consolidation = consolidated_manual ? 2 : 1;
! 	    } else
! 		no_sort_articles();
! 	} else
! 	    no_sort_articles();
  
  	cc_save = cancel_count;
  	cancel_count = 0;
*** ./LAST/format.awk	Fri Apr 12 19:28:32 1991
--- format.awk	Mon Apr 15 20:21:42 1991
***************
*** 15,22 ****
  	firstsh = 0
  
  	printf("From: %s\nSubject:", progname);
! 	for (i = 2; i <= NF; i++) printf(" %s", $i);
! 	printf("\n\n")
  
  	linebuf = indent = ""
  	curcol = indcol = 0
--- 15,29 ----
  	firstsh = 0
  
  	printf("From: %s\nSubject:", progname);
! 	if ($2 == "NAME") {
! 		getline
! 		for (i = 2; i <= NF; i++) printf(" %s", $i);
! 		printf("\n\n")
! 		print
! 	} else {
! 		for (i = 2; i <= NF; i++) printf(" %s", $i);
! 		printf("\n\n")
! 	}
  
  	linebuf = indent = ""
  	curcol = indcol = 0
*** ./LAST/global.c	Wed Feb  6 19:14:17 1991
--- global.c	Mon Apr 15 21:58:31 1991
***************
*** 179,187 ****
--- 179,191 ----
      signal(SIGPWR, catch_hangup);
  #endif
  
+ #ifdef CONFIG_NUM_IN_VERSION
      sprintf(version_id, "%s.%d #%d", RELEASE, PATCHLEVEL,
  #include "update.h"
  	    );
+ #else
+     sprintf(version_id, "%s.%d", RELEASE, PATCHLEVEL);
+ #endif
  
      user_id = getuid();
  
*** ./LAST/group.c	Fri Apr 12 19:29:28 1991
--- group.c	Wed Apr 17 09:21:14 1991
***************
*** 326,332 ****
      }
  
      if (mg_head != NULL) {
! 	if (!dont_sort_articles) sort_articles(-1);
  	init_group(mg_head);
      }
  
--- 326,335 ----
      }
  
      if (mg_head != NULL) {
! 	if (dont_sort_articles)
! 	    no_sort_articles();
! 	else
! 	    sort_articles(-1);
  	init_group(mg_head);
      }
  
***************
*** 464,470 ****
  	read_mode_group = NULL;
      
      mask = NULL;
! 
      if (command == K_GOTO_GROUP)
  	goto get_group_name;
  
--- 467,474 ----
  	read_mode_group = NULL;
      
      mask = NULL;
!     if (access_mode == 0) access_mode |= ACC_PARSE_VARIABLES;
!     
      if (command == K_GOTO_GROUP)
  	goto get_group_name;
  
***************
*** 792,797 ****
--- 796,802 ----
  
      if (access_group(gh, first, only_unread_articles ?
  		     gh->last_db_article : gh->current_first - 1,
+ 		     ACC_PARSE_VARIABLES |
  		     ACC_EXTRA_ARTICLES | ACC_ALSO_CROSS_POSTINGS |
  		     ACC_ALSO_READ_ARTICLES | ACC_ONLY_READ_ARTICLES,
  		     (char *)NULL) < 0) {
***************
*** 905,910 ****
--- 910,917 ----
      if (!seq_cross_filtering)
  	if (also_read_articles || mask || also_cross_postings)
  	    access_mode |= ACC_ALSO_CROSS_POSTINGS;
+     if (seq_cross_filtering && also_unsub_groups)
+ 	access_mode |= ACC_ALSO_UNSUB_GROUPS;
      if (dont_split_digests)
  	access_mode |= ACC_DONT_SPLIT_DIGESTS;
  
***************
*** 960,966 ****
      }
      merge_memory();
      if (n_articles == 0) return;
!     if (!dont_sort_articles) sort_articles(-1);
  
      dummy_group.group_flag = G_FAKED;
      dummy_group.master_flag = 0;
--- 967,976 ----
      }
      merge_memory();
      if (n_articles == 0) return;
!     if (dont_sort_articles)
! 	no_sort_articles();
!     else
! 	sort_articles(-1);
  
      dummy_group.group_flag = G_FAKED;
      dummy_group.master_flag = 0;
*** ./LAST/man/nn.1.A	Fri Apr 12 19:29:30 1991
--- man/nn.1.A	Thu Apr 18 18:23:32 1991
***************
*** 766,771 ****
--- 766,772 ----
  auto-select-closed,
  consolidated-menu,
  counter-delim-left, counter-delim-right,
+ counter-padding,
  save-closed-mode.
  .SH THE JUNK-ARTICLES AND LEAVE-NEXT COMMANDS
  The \fBJ\fP {\fBjunk-articles\fP} command is a very flexible command
*** ./LAST/man/nn.1.B	Fri Apr 12 19:28:34 1991
--- man/nn.1.B	Thu Apr 18 18:23:32 1991
***************
*** 905,916 ****
  \fB:sort\fP [ \fImode\fP ]
  Reorder the articles on the menu according to \fImode\fP or if omitted
  to the default \fBsort-mode\fP.  The following sorting modes are
! available: \fBarrival\fP (list articles in the order in which they
! arrived on the system), \fBsubject\fP (articles with identical
  subjects are grouped and ordered after age of the oldest article in
! the group), lexical (subjects in lexicographical order), \fBage\fP
! (articles ordered after posting date only), and \fBsender\fP (articles
! ordered after sender's name).
  .TP
  \fB:toggle\fP \fIvariable\fP
  Toggle a boolean variable.
--- 905,925 ----
  \fB:sort\fP [ \fImode\fP ]
  Reorder the articles on the menu according to \fImode\fP or if omitted
  to the default \fBsort-mode\fP.  The following sorting modes are
! available:
! .br
! \fBarrival\fP: list articles by local article number which
! will be the same as the order in which they
! arrived on the system (unless groups are merged),
! .br
! \fBsubject\fP: articles with identical
  subjects are grouped and ordered after age of the oldest article in
! the group,
! .br
! \fBlexical\fP: subjects in lexicographical order,
! .br
! \fBage\fP: articles ordered after posting date only,
! .br
! \fBsender\fP: articles ordered after sender's name.
  .TP
  \fB:toggle\fP \fIvariable\fP
  Toggle a boolean variable.
*** ./LAST/man/nn.1.C	Fri Apr 12 19:29:30 1991
--- man/nn.1.C	Thu Apr 18 18:23:32 1991
***************
*** 376,381 ****
--- 376,385 ----
  key.  (It will show the symbol <> to indicate that it is awaiting
  confirmation.)
  .TP
+ \fBconsolidated-manual\fP	(boolean, default false)
+ When set, the \fIonline manual\fP will be presented with one
+ menu line for each \fIprogram\fP in the \fInn\fP package.
+ .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
***************
*** 389,394 ****
--- 393,406 ----
  The delimiter string output to the right of the article counter in a
  closed subject's menu line.
  .TP
+ \fBcounter-padding\fP \fIpad\fP		(integer, default 5)
+ On a consolidated menu, the subjects may not be very well aligned
+ because the added [...] counters have varying length.  To (partially)
+ remedy this, all counters (and subjects without counters) are prefixed
+ by up to \fIpad\fP spaces to get better alignment.  Increasing it
+ further may yield practially perfect alignment at the cost of less
+ space for the subject itself.
+ .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
***************
*** 618,623 ****
--- 630,643 ----
  Otherwise, the articles in
  a folder will be presented in the sequence in which they were saved.
  .TP
+ \fBguard-double-slash\fP	(boolean, default false)
+ Normally, when entering a file name, entering two slashes `//' in a
+ row (or following a slash by a plus `/+') will cause \fInn\fP to
+ erase the entire line and replace it with the `/' (or `+').  On some
+ systems, two slashes are used in network file names, and on those
+ systems \fBguard-double-slash\fP can be set; that will cause \fInn\fP
+ to require \fIthree\fP slashes in a row to clear the input.
+ .TP
  \fBheader-lines\fP \fIlist\fP	(string, no default)
  When set, it determines the list of header fields that are shown when
  an article is read instead of the normal one line header showing the
***************
*** 754,759 ****
--- 774,792 ----
  check for arrival of new mail every minute or so by looking at the
  specified file.
  .TP
+ \fBmail-alias-expander\fP \fIprogram\fP	(string, default not set)
+ When set, aliases used in mail responses may be expanded by the
+ specified \fIprogram\fP.  The program will be given the completed
+ response in a file as its only argument, and the aliases should be
+ expanded directly in this file (of course the \fIprogram\fP may use
+ temporary files and other means to expand the aliases as long the the
+ result is stored in the provided file).
+ .br
+ Notice: currently there are no alias expanders delivered with \fInn\fP.
+ .br
+ Warning: Errors in the expansion process may lead to the response
+ not being sent.
+ .TP
  \fBmail-format\fP	(boolean, default false)
  When set, \fInn\fP will save articles in a format that is compatible
  with normal mail folders.
***************
*** 839,844 ****
--- 872,884 ----
  2: Add a blank line between \fIall\fP articles.
  .fi
  .TP
+ \fBmerge-report-rate\fP \fIrate\fP	(integer, default 1)
+ When \fInn\fP is invoked with the -m option (directly or via
+ \fInngrap\fP), a status report of the merging process is displayed and
+ updated on the screen every \fIrate\fP seconds.  The report contains
+ the time used so far and an estimate of the time needed to complete
+ the merge.
+ .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.
***************
*** 1277,1282 ****
--- 1317,1327 ----
  When set, \fInn\fP will sort articles according to the current
  \fBsort-mode\fP on entry to a group.  Otherwise, articles will be
  presented in order of arrival.
+ If not set on entry to a menu for merged groups, the articles from
+ each group will be kept together on the menu.  If \fBsort\fP is unset
+ while merged groups are presented on the menu, the articles will be
+ reordered by local article number (which may not keep articles from
+ the same group together).
  .TP
  \fBsort-mode\fP \fImode\fP	(integer, default 1)
  The default sort algorithm used to sort the articles on entry to a
***************
*** 1283,1289 ****
  news group.  It is a numeric value corresponding to one of the sorting
  methods described in connection with the :sort command:
  .br
! 	0 \- arrival (no sorting)
  .br
  	1 \- subject (subjects ordered after age of first article)
  .br
--- 1328,1334 ----
  news group.  It is a numeric value corresponding to one of the sorting
  methods described in connection with the :sort command:
  .br
! 	0 \- arrival (ordered by article number)
  .br
  	1 \- subject (subjects ordered after age of first article)
  .br
*** ./LAST/man/nnpost.1	Mon Jul 16 17:38:49 1990
--- man/nnpost.1	Thu Apr 18 10:49:33 1991
***************
*** 33,38 ****
--- 33,47 ----
  If a source file is specified with \fB\-f\fP it will be used as the
  initial article body.  If the \fB\-p\fP option is also specified, the
  article is posted directly without editing.
+ .LP
+ \fInnpost\fP can be used to do unattended postings if sufficient
+ arguments are provided on the command line to build the header and the
+ body of the article.  The required arguments are: one or more
+ newsgroups, a subject (\fB\-s\fP), a source file (\fB\-f\fP), a
+ distribution (\fB\-d\fP), and the \fB\-p\fP option.  Other fields
+ which are not specified (e.g. keywords) will not be included in the
+ header.  The contents of the \fInews-header\fP variable in the
+ init file will be included in the header.
  .SH OPTIONS
  .TP
  \fB\-d\fP \fIdistribution\fP
*** ./LAST/master.c	Fri Apr 12 19:28:36 1991
--- master.c	Mon Apr 15 18:56:15 1991
***************
*** 625,630 ****
--- 625,637 ----
  	    temp = 0;
  	else {
  	    int mpid;
+ 	    extern char proto_host[];
+ 
+ 	    if (proto_host[0]) {
+ 		printf("Can't kill master on another host (%s)\n", proto_host);
+ 		log_entry('R', "Attempt to kill master on host %s", proto_host);
+ 		exit(1);
+ 	    }
  
  	    for (temp = 10; --temp >= 0; sleep(3)) {
  		sleep(3);
*** ./LAST/menu.c	Fri Apr 12 19:29:32 1991
--- menu.c	Tue Apr 16 21:15:25 1991
***************
*** 37,42 ****
--- 37,43 ----
  export int  menu_spacing = 0;	    /* number of screen lines per menu line */
  export char *counter_delim_left  = "[";
  export char *counter_delim_right = "] ";
+ export int  counter_padding = 5; /* counters are padded to align subjects */
  
  export int  auto_preview_mode = 0; /* preview rather than select */
  export int  preview_continuation = 12; /* what to do after preview */
***************
*** 201,212 ****
      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;
  
--- 202,243 ----
      save_xy();
  }
  
+ static attr_type closed_attr(mi, cbuf)
+ register struct menu_info *mi;
+ char *cbuf;
+ {
+     char sel[10], unr[10];
+     attr_type cattr;
+     
+     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);
+ 
+     return cattr;
+ }
+ 
+ static int subj_indent;
+ 
  static mark()
  {
      register article_header *ah;
      register struct menu_info *mi;
      int lno, lnum, lsubj, lname;
!     int pad;
      char cbuf[80];
      attr_type cattr;
  
***************
*** 219,225 ****
  	lno = firstl + ah->menu_line;
  	gotoxy(0, lno);
  	putchar(ident[mi->mi_art_id]);
! 	only_counters = 0;
  	goto print_line;
      }
  
--- 250,256 ----
  	lno = firstl + ah->menu_line;
  	gotoxy(0, lno);
  	putchar(ident[mi->mi_art_id]);
! 	cattr = closed_attr(mi, cbuf);
  	goto print_line;
      }
  
***************
*** 229,234 ****
--- 260,268 ----
  
      if (ah->flag & A_CLOSED) {
  	struct menu_info old;
+ 	char oldctr[80];
+ 	attr_type oldattr;
+ 
  	mi = &menu_info[ah->menu_line];
  	old = *mi;
  	thread_counters(firsta + cura);
***************
*** 236,246 ****
  	    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;
--- 270,281 ----
  	    old.mi_selected == mi->mi_selected &&
  	    old.mi_unread == mi->mi_unread) return;
  
! 	cattr = closed_attr(mi, cbuf);
! 
! 	if (!slow_mode) goto print_line;
! 	oldattr = closed_attr(&old, oldctr);
! 	if (strcmp(cbuf, oldctr)) goto print_line;
! 	last_attr = cattr;
      }
  
      if (last_attr == ah->disp_attr) return;
***************
*** 270,297 ****
         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;
--- 305,313 ----
         4	id   subject  (or as 1 if short subject)
       */
  
!     if ((ah->flag & A_CLOSED) == 0) {
! 	cattr = ah->attr;
! 	cbuf[0] = NUL;
      }
  
      if (fmt_linenum > 4) fmt_linenum = 1;
***************
*** 316,323 ****
       case -1:
  	lsubj -= 9;
  	so_printf("%-8.8s ", ah->sender);
! 	break;
! 
       case 0:
  	lsubj -= NAME_LENGTH + 1 + 2 + lnum;  /* name. .subj. +.lines */
  	so_printf("%-*s ", NAME_LENGTH, ah->sender);
--- 332,339 ----
       case -1:
  	lsubj -= 9;
  	so_printf("%-8.8s ", ah->sender);
! 	goto no_counters;
! 	
       case 0:
  	lsubj -= NAME_LENGTH + 1 + 2 + lnum;  /* name. .subj. +.lines */
  	so_printf("%-*s ", NAME_LENGTH, ah->sender);
***************
*** 356,364 ****
  	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("-");
--- 372,391 ----
  	so_printf("%s", cbuf);
  	if (cattr == A_KILL) so_end();
  	so_printf("%s", counter_delim_right);
! 	pad = strlen(cbuf) + strlen(counter_delim_left) + strlen(counter_delim_right);
! 	if (cattr == A_KILL) pad += cookie_size * 2;
! 	lsubj -= pad > subj_indent ? pad : subj_indent;
! 	pad = subj_indent - pad;
!     } else {
! 	lsubj -= subj_indent;
! 	pad = subj_indent;
      }
!     if (pad > 0) {
! 	if (pad > 20) pad = 20;
! 	so_printf("                    "+20-pad);
!     }
! 
!  no_counters:
      if (!fmt_rptsubj && lno > firstl && ah->flag & A_SAME) {
  	if (ah->replies == 0 || prt_replies(ah->replies) == 0)
  	    so_printf("-");
***************
*** 381,387 ****
  	so_printf(ah->lines >= 0 ? " +%d" : " +?", ah->lines);
  
      so_end();
!     if (ah->flag & A_CLOSED) clrline();
  
   out:
      ah->disp_attr = last_attr;
--- 408,414 ----
  	so_printf(ah->lines >= 0 ? " +%d" : " +?", ah->lines);
  
      so_end();
!     if (ah->flag & A_CLOSED) clrline_noflush();
  
   out:
      ah->disp_attr = last_attr;
***************
*** 1045,1050 ****
--- 1072,1079 ----
      clrpage(firstl + menu_length);
  
   draw_menu:
+     subj_indent = consolidate ? counter_padding : 0;
+ 
      if (!s_keyboard) {
  	int first_menu_line = menu_length;
  
***************
*** 1136,1145 ****
  
    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();
--- 1165,1176 ----
  
    same_prompt:
  
!     if (cura < 0 || cura > numa) cura = 0;
  
!     if (!IS_VISIBLE(articles[firsta+cura])) {
! 	cura = next_root_article(firsta + cura) - firsta;
! 	if (cura > numa) cura = 0;
!     }
  
       if (numa >= 0) {
  	 cursor_at_id();
***************
*** 1859,1865 ****
  	     article_id = tmp;
  	 }
  
! 	 repl_attr(article_id, cura, A_KILL, last_attr, 1);
  	 goto Prompt;
  
        case K_AUTO_SELECT:
--- 1890,1896 ----
  	     article_id = tmp;
  	 }
  
! 	 repl_attr(firsta+article_id, firsta+cura+1, A_KILL, last_attr, 1);
  	 goto Prompt;
  
        case K_AUTO_SELECT:
*** ./LAST/newsrc.c	Fri Apr 12 19:29:32 1991
--- newsrc.c	Thu Apr 18 18:16:19 1991
***************
*** 352,357 ****
--- 352,393 ----
      }
  
      rc = open_file(newsrc_file, OPEN_READ);
+     if (rc != NULL) {
+ 	fseek(rc, (off_t)0, 2);
+ 	if (ftell(rc))
+ 	    rewind(rc);
+ 	else {
+ 	    fclose(rc);
+ 	    rc = NULL;
+ 	}
+     }
+ 
+     if (rc == NULL) {
+ 	sprintf(bak, "%s%s", newsrc_file, bak_suffix ? bak_suffix : ".bak");
+ 	if (rc = open_file(bak, OPEN_READ)) {
+ 	    int ans;
+ 	    time_t rc_mtime;
+ 	    
+ 	    printf("\nThere is no .newsrc file - restore backup? (y) ");
+ 	    if ((ans = yes(0)) < 0) nn_exit(1);
+ 	    if (!ans) {
+ 		printf("\nConfirm building .newsrc from scratch: (y/n) ");
+ 		if ((ans = yes(1)) <= 0) nn_exit(1);
+ 		fclose(rc);
+ 		rc = NULL;
+ 	    }
+ 	    switch (new_group_action) {
+ 	     case RCX_TIME:
+ 	     case RCX_TIME_CONF:
+ 	     case RCX_RNLAST:
+ 		last_new_group = get_last_new();
+ 		rc_mtime = m_time(rc);
+ 		if (last_new_group < 0 || rc_mtime < last_new_group)
+ 		    last_new_group = rc_mtime;
+ 	    }
+ 	}
+     }
+ 
      if (rc == NULL) {
  	extern FILE *open_file_search_path();
  
***************
*** 1365,1373 ****
      register int i;
      int header = 1;
  
!     re = newobj(regexp *, grep_patterns);
!     for (i = 0; i < grep_patterns; i++)
! 	re[i] = regcomp(pat[i]);
  
      Loop_Groups_Sorted(gh) {
  	if (gh->master_flag & M_IGNORE_GROUP) continue;
--- 1401,1411 ----
      register int i;
      int header = 1;
  
!     if (grep_patterns > 0) {
! 	re = newobj(regexp *, grep_patterns);
! 	for (i = 0; i < grep_patterns; i++)
! 	    re[i] = regcomp(pat[i]);
!     }
  
      Loop_Groups_Sorted(gh) {
  	if (gh->master_flag & M_IGNORE_GROUP) continue;
***************
*** 1517,1522 ****
--- 1555,1565 ----
  	if (!silent) printf("%s%s\n", why, gh->group_name);
      }
  
+     if (no_update) {
+ 	if (changed) printf("Files were NOT updated\n");
+ 	return;
+     }
+ 
      if (changed) {
  	newsrc_update_freq = 0;
  	dump_newsrc();
***************
*** 1592,1597 ****
--- 1635,1642 ----
      total = groups = 0;
  
      if (goback_interact) {
+ 	if (no_update) printf("Warning: changes will not be saved\n");
+ 	init_term(1);
  	raw();
      }
  
***************
*** 1632,1637 ****
--- 1677,1685 ----
  
   out:
  
+     if (goback_interact)
+ 	unset_raw();
+ 
      if (total == 0) {
  	printf("No articles marked\n");
  	return;
***************
*** 1644,1649 ****
--- 1692,1699 ----
  	printf("%ld article%s marked unread in %d group%s\n",
  	       (long)total, plural((long)total),
  	       groups, plural((long)groups));
+ 
+     if (no_update) printf("No files were updated\n");
  }
  
  /* fake this for read_active_file */
*** ./LAST/nn.c	Wed Feb  6 19:14:21 1991
--- nn.c	Thu Apr 18 18:19:22 1991
***************
*** 612,618 ****
  
  static prt_version()
  {
!     printf("Release %s,  Kim F. Storm, 1990\n\n", version_id);
  }
  
  display_motd(check)
--- 612,618 ----
  
  static prt_version()
  {
!     printf("Release %s,  Kim F. Storm, 1991\n\n", version_id);
  }
  
  display_motd(check)
***************
*** 873,885 ****
  	break;
  
       case I_AM_TIDY:
! 	init_term(1);
  	visit_init_file(0, (char *)NULL);
  	group_name_args = opt_nntidy(argc, argv);
  	break;
  
       case I_AM_GOBACK:
! 	init_term(1);
  	visit_init_file(0, (char *)NULL);
  	group_name_args = opt_nngoback(argc, &argv);
  	break;
--- 873,885 ----
  	break;
  
       case I_AM_TIDY:
! 	init_term(0);
  	visit_init_file(0, (char *)NULL);
  	group_name_args = opt_nntidy(argc, argv);
  	break;
  
       case I_AM_GOBACK:
! 	init_term(0);
  	visit_init_file(0, (char *)NULL);
  	group_name_args = opt_nngoback(argc, &argv);
  	break;
*** ./LAST/pack_date.c	Wed Feb  6 19:14:21 1991
--- pack_date.c	Wed Apr 17 08:32:40 1991
***************
*** 17,24 ****
  
  /* #define DATE_TEST /* never define this !! */
  
- #ifndef USE_OLD_TZ_CODE
- 
  #undef W
  #undef E
  #undef DST
--- 17,22 ----
***************
*** 192,282 ****
      }
      return adjust;
  }
- 
- #else
- 
- static long wtz(h, m, c)
- int  h, m;
- char c;
- {
-     if (c != 's') h--;		/* daylight savings */
-     return h*60L+m*30L;
- }
- 
- static long etz(h, m, c)
- int  h, m;
- char c;
- {
-     if (c == 's') h++;		/* summer time */
-     return -(h*60L+m*30L);
- }
- 
- #define TOLOWER(ch) (isupper(ch) ? tolower(ch) : (ch))
- 
- static long tzone(date)
- register char *date;
- {
-     char ch1, ch2, ch3, ch4;
-     long tz, wtz(), etz();
- 
-     while (*date && !isalpha(*date)) date++;
-     if (*date == NUL || *date == 'G') return 0;
- 
-     ch1 = TOLOWER(*date);
-     while (date++, (ch2 = TOLOWER(*date)) == '.')	/* p.s.t. -> pst */
- 	;
-     while (date++, (ch3 = TOLOWER(*date)) == '.')
- 	;
-     while (date++, (ch4 = TOLOWER(*date)) == '.')
- 	;
-     switch (ch1) {
-      case 'n':
- 	tz = wtz(3,1,0); break;		/* Newfoundland: nst */
-      case 'a':
- 	if (ch2 == 'e') {
- 	    tz = etz(10,0,ch4); break;	/* Australian Eastern: aest, aesst */
- 	}
- 	if (ch2 == 'c') {
- 	    tz = etz(9,1,ch4); break;	/* Australian Central: acst, acsst */
- 	}
- 	if (ch2 == 'w') {
- 	    tz = etz(8,1,ch4); break;	/* Australian Western: awst, awsst */
- 	}
- 	tz = wtz(4,0,ch2); break;	/* Atlantic: ast, adt */
-      case 'b':
- 	tz = etz(1,0,0); break;		/* British summer: bst */
-      case 'c':
- 	tz = wtz(6,0,ch2); break;	/* Central: cst, cdt */
-      case 'e':
- 	if (ch2 == 'e') {
- 	    tz = etz(0,0,ch3); break;	/* European Eastern: eet, eest */
- 	}
- 	tz = wtz(5,0,ch2); break;	/* Eastern: est, edt */
-      case 'g':
- 	tz = etz(0,0,0); break;		/* Greenwich: gmt */
-      case 'h':
- 	tz = wtz(10,0,ch2); break;	/* Hawaii: hst, hdt */
-      case 'j':
- 	tz = etz(9,0,0); break;		/* Japan: jst */
-      case 'm':
- 	if (ch2 == 'e') {
- 	    tz = etz(1,0,ch3); break;	/* Middle European: met, mest */
- 	}
- 	tz = wtz(7,0,ch2); break;	/* Mountain: mst, mdt */
-      case 'p':
- 	tz = wtz(8,0,ch2); break;	/* Pacific: pst, pdt */
-      case 'w':
- 	tz = etz(2,0,ch3); break;	/* Western European: wet, west */
-      case 'y':
- 	tz = wtz(9,0,ch2); break;	/* Yukon: yst, ydt */
-      default:
- 	tz = 0; break;		/* use GMT if we can't understand it */
-     }
- 
-     return tz;
- }
- 
- #endif
  
  static next_int(dp)
  char **dp;
--- 190,195 ----
*** ./LAST/patchlevel.h	Fri Apr 12 19:29:33 1991
--- patchlevel.h	Thu Apr 18 10:56:06 1991
***************
*** 26,32 ****
   *	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
  
--- 26,33 ----
   *	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
+  *	1991-04-18: Patch #16 (6.4.16) - HIGH
   */
  
! #define PATCHLEVEL 16
  
*** ./LAST/prefix.c	Fri Apr 12 19:28:41 1991
--- prefix.c	Mon Apr 15 22:28:07 1991
***************
*** 30,37 ****
--- 30,41 ----
  #else
  	fprintf(f, "#!%s\n", SHELL);
  #endif
+ #ifdef PUT_TIMESTAMP_IN_SCRIPTS
  	fprintf(f, "\n# Generated by nn release %s at %s\n\n",
  	       version_id, date_time((time_t)0));
+ #else
+ 	fprintf(f, "\n# Generated by nn release %s\n\n", version_id);
+ #endif
      }
      fprintf(f, "VERSION=\"%s\"\n", version_id);
  
*** ./LAST/sequence.c	Fri Oct  5 19:07:18 1990
--- sequence.c	Tue Apr 16 21:50:25 1991
***************
*** 155,161 ****
      backp = NULL;
      seq_ix = 0;
      Loop_Groups_Sequence(gh) {
! 	gh->preseq_index = (gh->group_flag & G_UNSUBSCRIBED) ? 0 : ++seq_ix;
  	gh->prev_group = backp;
  	backp = gh;
      }
--- 155,161 ----
      backp = NULL;
      seq_ix = 0;
      Loop_Groups_Sequence(gh) {
! 	gh->preseq_index = ++seq_ix;
  	gh->prev_group = backp;
  	backp = gh;
      }
*** ./LAST/sort.c	Fri Apr 12 19:29:33 1991
--- sort.c	Mon Apr 15 19:22:09 1991
***************
*** 265,270 ****
--- 265,288 ----
  	quicksort(articles, n_articles, article_header *, order_date_subj_date);
  }
  
+ /*
+  *	If articles are not sorted via sort_articles, they must still be
+  *	marked with proper attributes (e.g. A_ROOT_ART) via no_sort_articles.
+  */
+ 
+ no_sort_articles()
+ {
+     register article_number n;
+     register article_header *ah;
+     extern int bypass_consolidation;
+ 
+     for (n = n_articles; --n >= 0;) {
+ 	ah = articles[n];
+ 	ah->flag &= ~(A_SAME|A_ALMOST_SAME|A_NEXT_SAME|A_ROOT_ART);
+ 	ah->flag |= A_ROOT_ART;
+     }
+     bypass_consolidation = 1;
+ }
  
  /*
   * Eliminate articles with the A_KILL flag set preserving the present ordering.
*** ./LAST/term.c	Fri Apr 12 19:28:42 1991
--- term.c	Thu Apr 18 10:08:19 1991
***************
*** 42,47 ****
--- 42,48 ----
  export int  use_visible_bell = 1; /* if supported by terminal */
  export int  ignore_xon_xoff = 1;
  export int  multi_key_guard_time = 2; /* tenths of a second */
+ export int  guard_double_slash = 0; /* need /// or //+ to clear line */
  
  export key_type help_key = '?';
  export key_type comp1_key = SP;
***************
*** 353,360 ****
  	return;
      }
  
!     if ((term_name = getenv("TERM")) == NULL)
! 	user_error("No TERM variable in environment");
  
      if (!full) return;
  
--- 354,365 ----
  	return;
      }
  
!     if ((term_name = getenv("TERM")) == NULL) {
! 	if (full)
! 	    user_error("No TERM variable in environment");
! 	else
! 	    term_name = "unknown";
!     }
  
      if (!full) return;
  
***************
*** 556,561 ****
--- 561,573 ----
      fl;
  }
  
+ clrline_noflush()
+ {
+     BATCH_CHECK;
+ 
+     putp(clr_eol);
+ }
+ 
  clrpage(lineno)
  register int lineno;
  {
***************
*** 1255,1265 ****
  	if (i == max) continue;
  
  	if (i > 0 && buf[i-1] == '/' && (c == '/' || c == '+')) {
! 	  extern int file_completion();
! 	  if (completion == file_completion) {
! 	    while (i > 0) { putchar(BS); i--; }
! 	    clrline();
! 	  }
  	}
  
  	putchar(c);
--- 1267,1280 ----
  	if (i == max) continue;
  
  	if (i > 0 && buf[i-1] == '/' && (c == '/' || c == '+')) {
! 	    if (c != '/' || !guard_double_slash || (i > 1 && buf[i-2] == '/')) {
! 		extern int file_completion();
! 
! 		if (completion == file_completion) {
! 		    while (i > 0) { putchar(BS); i--; }
! 		    clrline();
! 		}
! 	    }
  	}
  
  	putchar(c);
***************
*** 1569,1576 ****
  	msg_ptr = msg_stack;
      }
  
!     BATCH_CHECK;
! 
      gotoxy(0, Lines-1);
      fputs(errmsg, stdout);
      clrline();
--- 1584,1595 ----
  	msg_ptr = msg_stack;
      }
  
!     if (terminal_speed == 0) {
! 	printf("%s\n", errmsg);
! 	fl;
! 	return;
!     }
!     
      gotoxy(0, Lines-1);
      fputs(errmsg, stdout);
      clrline();
*** ./LAST/variable.c	Fri Apr 12 19:29:34 1991
--- variable.c	Tue Apr 16 23:21:00 1991
***************
*** 80,85 ****
--- 80,86 ----
      included_mark[],
      *inews_program,
      *initial_newsrc_path,
+     *mail_alias_expander,
      *mail_box,
      *mail_record,
      *mail_script,
***************
*** 122,128 ****
--- 123,131 ----
      conf_dont_sleep,
      conf_group_entry,
      conf_junk_seen,
+     consolidated_manual,
      consolidated_menu,
+     counter_padding,
      delay_redraw,
      dflt_kill_select,
      do_kill_handling,
***************
*** 140,145 ****
--- 143,149 ----
      fmt_rptsubj,
      folder_format_check,
      folder_rewrite_trace,
+     guard_double_slash,
      ignore_xon_xoff,
      include_art_id,
      include_full_header,
***************
*** 210,215 ****
--- 214,220 ----
      mark_read_return,
      mark_read_skip,
      menu_spacing,
+     merge_report_rate,
      message_history,
      min_pv_window,
      multi_key_guard_time,
***************
*** 319,327 ****
--- 324,334 ----
      "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-manual",	BOOL 0,		(char **)&consolidated_manual,
      "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,
+     "counter-padding",		INT 1,		(char **)&counter_padding,
      "cross-filter-seq",		BOOL 0,		(char **)&seq_cross_filtering,
      "cross-post",		BOOL 0,		(char **)&also_cross_postings,
      "data-bits",		INT 0,		(char **)&data_bits,
***************
*** 353,358 ****
--- 360,366 ----
      "follow-distribution",	STR 0,		(char **)&distribution_follow,
      "from-line-parsing",	INT 0,		(char **)&strict_from_parse,
      "fsort",			BOOL 2,		(char **)&dont_sort_folders,
+     "guard-double-slash",	BOOL 0,		(char **)&guard_double_slash,
      "header-lines",		STR 0,		(char **)&header_lines,
      "help-key",			KEY 0,		(char **)&help_key,
      "ignore-xon-xoff",		BOOL 0,		(char **)&ignore_xon_xoff,
***************
*** 375,380 ****
--- 383,389 ----
      "long-menu",		BOOL 1,		(char **)&long_menu,
      "macro-debug",		BOOL 0,		(char **)&macro_debug,
      "mail",			STR 2,		(char **)&mail_box,
+     "mail-alias-expander",	STR 0,		(char **)&mail_alias_expander,
      "mail-format",		BOOL 0,		(char **)&use_mail_folders,
      "mail-header",		STR 0,		(char **)&extra_mail_headers,
      "mail-record",		STR 2,		(char **)&mail_record,
***************
*** 387,392 ****
--- 396,402 ----
      "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,
+     "merge-report-rate",	INT 0,		(char **)&merge_report_rate,
      "message-history",		INT 0,		(char **)&message_history,
      "min-window",		INT 1,		(char **)&min_pv_window,
      "mmdf-format",		BOOL 0,		(char **)&use_mmdf_folders,
***************
*** 661,672 ****
  	    return 1;
  
  	 case 2:
! 	    if (BOOL_VAR == on) {
! 		BOOL_VAR = !on;
! 		if (!in_init) {
! 		    sort_articles(BOOL_VAR ? 0 : -1);
! 		    return 1;
! 		}
  	    }
  	    break;
  
--- 671,685 ----
  	    return 1;
  
  	 case 2:
! 	    if (BOOL_VAR) {	/* don't change if already ok */
! 		if (!on) break;
! 	    } else
! 		if (on) break;
! 
! 	    BOOL_VAR = !on;
! 	    if (!in_init) {
! 		sort_articles(BOOL_VAR ? 0 : -1);
! 		return 1;
  	    }
  	    break;
  
-- 
Kim F. Storm  <storm@texas.dk>		No news is good news,
Texas Instruments A/S, Denmark		  but nn is better!