/*
**++
**  FACILITY:
**      NEWSUTILITY
**
**  ABSTRACT:
**      This module contain common support routines used by all other modules
**      of NEWS.
**
**  AUTHOR:
**      Geoff Huston
**
**  COPYRIGHT:
**      Copyright © 1988,1989,1990,1991,1992
**
**  VERSION:
**	V6.0-1	11/12/90	glass@mgi.com
**	The changes here are confined to do_new_item.  The principal change is
**	the the fnam parameter (the pointer to a filename) is now allowed to be
**	NULL.  When fnam is not NULL, then do_new_item functions as before.  When
**	fnam is NULL, then do_new_item will call create_article when it determines
**	the destination filename and the xref header line.
**
**	V6.0-3	31/1/91		glass!@mgi.com
**	Retain status var on duplicate item record check in do_new_item
**	V6.1	 3-Feb-1992	rankin@eql.caltech.edu
**			substantial lint cleanup from gcc -Wall
**	V6.1	15-Feb-1992	rankin@eql.caltech.edu
**			Optimize group name lookups.
**	V6.1	13-Apr-92	Marc Shannon <synful@DRYCAS.CLUB.CC.CMU.EDU>
**	  - byte comparisons for speedup
**	V6.1    21-Jul-92	Bill Fenner <wcf@ecl.psu.edu>
**	  - improved error reporting
**	V6.1    17-Feb-1993  bailey@genetics.upenn.edu
**	  - changed 'unseenitems' to 'unreaditems' throughout
**	V6.1b7  15-May-1993  Charles Bailey  bailey@genetics.upenn.edu
**	  - optimized item number lookup, handling of message-IDs
**	  - changed map_items() to display true group count in grp dir list
**	V6.1b7  15-Jun-1993  bailey@genetics.upenn.edu
**	 - set record size in grprab to NEWS_GRPFIL_RSZ, not sizeof newsgrp,
**	   since newsgrp struct contains volatile data not needed in file,
**	   and zero fields in new grp struct beyond end of data in file
**	V6.1b7  20-Jun-1993  bailey@genetics.upenn.edu
**	 - fix bug in find_itm_by_num
**	V6.1b7	 8-Aug-1993  bailey@genetics.upenn.edu
**	  - added mem_fail() as part of new mem alloc routines
**	V6.1b8   9-Sep-1993  mark.martinec@ijs.si
**	 - replace cli_catch() with more general news_condition_handler()
**	V6.1b8  22-Dec-1993  mark.martinec@ijs.si
**	 - convert calls to RMS to use new sys_* macros for error handling 
**	 - set variable 'session_is_interactive' according to kb device class
**	 - declare local variable 'status' in most of the support routines
**	   to enable higher level routines to use global 'status' value even
**	   across a call to a support routine (although reliance on global
**	   variable 'status' to remain unchanged across a call to some other
**	   routine is not recommended, some old parts of code still do rely
**	   on it).
**	V6.1b8  24-Jan-1994  mark.martinec@ijs.si
**       - new routine smg_put_chars to simplify calls to smg$put_chars
**         and to avoid numerous problems where nonprintable characters
**         appear in text or headers of news items;
**       - display_brdcst(): special handling for multi-line messages
**         and for BELL characters included in the message;
**       - fudge with 'last_cond' in news_condition_handler to avoid
**	   bug in VMSMAIL (incorrect handling of SS$_UNWIND)
**	V6.1	 2-SEP-1993  ewilts@galaxy.gov.bc.ca
**	  - allow for anonymous logging if NEWS_USAGE_ANONYMOUS is defined
**	    /exec in the default table (normally /sys)
**	    Logging will be done by ACCOUNT field instead of username
**	V6.1	 2-SEP-1993 ewilts@galaxy.gov.bc.ca
**	  - don't write out a news_usage_log record for entries with 0 articles read + posted
**	V6.1	 2-SEP-1993 ewilts@galaxy.gov.bc.ca
**	 - change default extension quantity on news_usage_log to 50.
**	V6.1b8   5-Sep-1993  terry@spcvxa.spc.edu
**	 - added XOVER support for obtaining item headers from nntp server
**	V6.1b8	 5-OCT-1993  ewilts@galaxy.gov.bc.ca
**	 - fixed account field length in log_to_usage_file()
**         looping during handler unwind.
**	V6.1b8   3-Mar-1994  mark.martinec@ijs.si
**	 - added a grp_vd resizing hack to screen_map_dir()
**	   to prevent ANU (as a client) writing beyond virtual screen
**	   when in FASTLOAD mode and DIR/ALL is selected.
**	   (final touches on that code: 17-Mar-1994)
**	V6.1b8  17-Mar-1994  mark.martinec@ijs.si
**	 - modify gen_id() to avoid trying (and failing) to read NEWS.GROUPS
**	   file when run as a client
**	V6.1b8  21-Mar-1994  mark.martinec@ijs.si
**	 - do_quit() and do_abort() now call closefiles_opt() instead
**	   of having their own modified copy of code from closefiles().
**	   This now also handles properly the case where ANU as a client
**	   wanted to close nonexisting ITEM and GROUPS files on Quit.
**	V6.1b8  12-Apr-1994  mark.martinec@ijs.si
**	 - do_new_item() receives an extra argument to disable some
**	   cases of signalling errors
**	V6.1b8  20-Apr-1994  mark.martinec@ijs.si
**	 - replaced on_error_continue (boolean) with on_error (multivalued)
**	V6.1b8  19-May-1994  mark.martinec@ijs.si
**	 - look for a file or logical name in smg$load_key_defs() corectly
**	V6.1b9  22-May-1994  Charles Bailey  bailey@genetics.upenn.edu
**	 - fix memory usage bug which snuck into 6.1b9 release with another patch
**	V6.1b9  25-May-1994  Charles Bailey  bailey@genetics.upenn.edu
**	 - add flk to do_openi and do_oitem to allow skim to ignore
**	   contention for item files
**	V6.1b9  25-May-1994  Pat Rankin  rankin@eql.caltech.edu
**	 - avoid use of `union SMGDEF'; module $SMGDEF is hopelessly mangled
**	V6.1b9  27-May-1994  Charles Bailey  bailey@genetics.upenn.edu
**	 - check that group has idmap list before using it in get_itm_by_ident()
**	V6.1b9   1-Jun-1994  Mark Martinec   mark.martinec@ijs.si
**	 - ignore SMG$_NONBRDMSG messages in display_brdcst()
**	V6.1b9   7-Jun-1994  Charles Bailey  bailey@genetics.upenn.edu
**	 - fix bug in map_items() which caused grp_topnum to be set incorrectly
**	   under some circumstances (particularly when group has only one item)
**	V6.1b9  20-Jun-1994  Mark Martinec   mark.martinec@ijs.si
**	 - don't take MAIL$_TEXT seriously in news_condition_handler()
**	 - introduce variable 'signalled_error_count' in order to limit
**	   the infinite loops through the condition handler
**	V6.1b9  23-Jun-1994  Mark Martinec   mark.martinec@ijs.si
**	  - applied code cleanup patch (gcc) by Pat Rankin (rankin@eql.caltech.edu)
**	V6.1b9	17-Aug-1994     Mark Martinec   mark.martinec@ijs.si
**	  - code cleanup to make it compile under gcc 2.6.0 with full
**	    warnings reporting turned on - with no or very few harmless warnings
**	V6.1b9	17-Sep-1994     Mark Martinec   mark.martinec@ijs.si
**	  - code cleanup to preserve the read-only nature of string literals
**	    (strategically placed 'const' attribute to string parameters)
**	  - in map_items: assure that negative number is not assigned
**	    to unsigned gap->grp_iasize -- this led later to
**	    news_malloc(very_very_large_number) and subsequent crash
**	V6.1b9  14-Nov-1994     Mark Martinec   mark.martinec@ijs.si
**	  - in routine do_new_item() replace statically allocated xrefline
**	    with a dynamic one to avoid memory corruption during processing
**	    of articles with long 'Xref:' lists
**	V6.1b9  29-Dec-1994     Mark Martinec   mark.martinec@ijs.si
**	  - added some type casting to news_*alloc() calls
**	  - in map_items: additional check for validity of max item number
**	    against grp_topnum
**	V6.1b9	08-Jan-1995  Charles Bailey  bailey@genetics.upenn.edu
**	  - update item index display in get_itm_by_num()
**	V6.1b9  15-Jan-1995  Charles Bailey  bailey@genetics.upenn.edu
**	  - add support for grp_funread field in ga records
**	V6.1b10	30-May-1995	Mark Martinec   mark.martinec@ijs.si
**	  - write routine fit_line to cope with the problem of truncated
**	    'Unread:' count in the 'Group:' header line of the items display
**      V6.1b10 27-Apr-1995  Bob Sloane sloane@kuhub.cc.ukans.edu
**        - added Kent Covert's definitions for left and right arrow keys.
**      V6.1b10 17-Aug-1995	Mark Martinec   mark.martinec@ijs.si
**	  - modify xoverni() to avoid dereferencing NULL pointer in case
**	    of invalid input.
**      V6.1b10 23-Aug-1996	Mark Martinec   mark.martinec@ijs.si
**	  - map_items tried to allocate unreasonably large initial memory chunk
**	    for gap->grp_ia in case when user had once peeked at a newsgroup
**	    with heavy traffic and never visited it again. The attempted
**	    allocation was for all items (now mostly nonexistent) from
**	    the very old and small grp_funread (from the user's newsrc file)
**	    up to the current gap->grp_topnum (now very high).
**	    Solution: clip the initial gap->grp_iasize to gap->grp_count.
**	    It is just an initial allocation - it will grow later if necessary.
**	    The symptom was unreasonably large virtual memory usage,
**	    or: %NEWS-E-ASSERT, Assert failed, expr not true:
**	                        "size < MALLOC_SANITY_LIMIT"
**--
**/

#ifdef vaxc
#module NEWSUTILITY "V6.1"
#endif

#define _NEWSUTILITY_C
#define module_name "NEWSUTILITY"

#include "newsinclude.h"
#include "newsextern.h"

#if NAKED_INCLUDES
#include stsdef
#include psldef
#include libdef
#include strdef
#include otsdef
#include shrdef
#include uaidef
#else
#include <stsdef.h>
#include <psldef.h>
#include <libdef.h>
#include <strdef.h>
#include <otsdef.h>
#include <shrdef.h>
#include <uaidef.h>
#endif

/* err_msg if filled by putmsg_action_routine with
  (possibly multi-line) message provided by $PUTMSG;
  lines are separated by '\0';  it is guaranteed
  that even the last (possibly truncated) line is
  terminated by '\0' within the declared array size */
static volatile char err_msg[1024];
static volatile int err_msg_l     = 0;  /* current length of err_msg */
static volatile int err_msg_lines = 0;  /* number of lines in err_msg */

static int screen_displays_setup = 0;

extern char dir_sincestr[];


extern
int mailfile_open;

char nntp_currgroup[132];

extern
int profile_flags;

#ifndef _stdlib_h	/* old gcc header has bad prototype */
extern int sleep __ARGS((unsigned));
#endif

/*
 *  nosysprv
 *
 *  Turn off image installed sysprv - BUT if user already had sysprv,
 *  then leave it on (as there is no change in user functionality)
 */

void nosysprv()
{
  unsigned int authprivs[2], msysprv[2] ;
  int item = JPI$_PROCPRIV;
  int status;

  msysprv[0] = PRV$M_SYSPRV;
  msysprv[1] = 0;
  _c$cks(lib$getjpi(&item,0,0,&authprivs[0],0,0));
  if (!(authprivs[0] & PRV$M_SYSPRV)) _c$cks(sys$setprv(0,msysprv,0,0));
  sysprv_off = 1;
}

/*
 *  sysprv
 *
 *  Turn sysprv on as a temp priv - assumes image was installed with SYSPRV
 */

void sysprv()
{
  unsigned int msysprv[2];
  int status;

  msysprv[0] = PRV$M_SYSPRV;
  msysprv[1] = 0;
  _c$cks(sys$setprv(1,msysprv,0,0));
  sysprv_off = 0;
}

/*
 *  fit_line
 *
 *  Modify the string 'str' to fit into a suggested 'width'
 *  by shrinking/expanding whitespace (glue).
 *  If 'width' < 0 the string width is left at its natural size.
 *
 *  Glue may be given implicitly (spaces in the string) or explicitly
 *  as '[nxc]' where n, x and c are single digits which represent
 *  the glue natural size, stretchability and shrinkability respectively.
 *  The units are 'the width of one character', but for values >5
 *  a progressive scale is used: see array 'unit_factors'.
 *
 *  Unlike in TeX, multiple consecutive spaces are not shrunk into one space.
 *
 *  Returns the difference: resulting_string_length - requested_length
 *  which is normally 0, but may be different if stretchability/shrinkability
 *  constraints of the given string would be exceeded.
 *
 *  The algorithm is a simplified implementation of the glue-setting
 *  algorithm described in: D. Knuth: The TeXbook, chapter "Glue".
 *
 *  (The algorithm used is probably an overkill, but does the tough job
 *  very nicely and may find other uses in ANU-NEWS.  The problem that it
 *  needed to solve in the first place was to fit the 'Group' item header line
 *  in display, which was often truncated not showing the 'Unread:' value.)
 *      May 1995,  Mark Martinec  <mark.martinec@nsc.ijs.si>
 */
int fit_line(str,str_l,str_m,width)
  char *str;  /* string to be modified (not necessarily '\0'-terminated,
                   but guaranteed to end up '\0'-terminated within str_m
                   upon return */
  int str_l;  /* initial string length if known, otherwise -1 */
  int str_m;  /* maximum allocated string size (including final '\0') */
  int width;  /* required final string length (should be <= str_m-1)  */
              /* N.B.: final string may be shorter or longer than width,
                       depending on its stretchability/shrinkability */
{
#define div_rounded(a,b) (((a)>=0 ? (a)+(b)/2 : (a)-(b)/2) / (b))
#define div_floor(a,b)   (tmp_q = (a)/(b), (tmp_q*(b) > (a) ? tmp_q-1 : tmp_q))
#define gunits(n) (10*unit_factors[(unsigned)(n) - (unsigned)'0'])
#define MAX_GLUE 48                               /* max number of glue blobs */

  static const int unit_factors[10] = {0,1,2,3,4,5,10,50,1000,100000};
  static int sfcode_initialized = 0;
  static short sfcode[256];               /* space factors for each character */
  int nonglue_characters, glue_natural_width, entire_natural_width, stretch_by;
  int total_glue_spaces, last_was_glue, j, k, tmp_q;
  int space_factor;     /* controls the default interword glue, normally 1000 */
  int initial_spaces;   /* flag to make initial spaces rigid */
  int glue_l;                 /* number of glue blobs */
    /* 10 glue units represent the width of one character */
  int glue_index[MAX_GLUE];   /* position of glue within the string */
  int glue_size[MAX_GLUE];    /* glue natural size */
  int glue_stretch[MAX_GLUE]; /* glue stretchability */
  int glue_shrink[MAX_GLUE];  /* glue shrinkability */
  char *cp;

  str_l = (str_l>=0) ? str_l : strlen(str);
  str_l = min(str_l,str_m);

  if (!sfcode_initialized) {
    for (k=0; k<256; k++) sfcode[k] = 1000;
    for (k='A'; k<='Z'; k++) sfcode[k] = 999;
    for (k=192; k<=222; k++) sfcode[k] = 999;
    sfcode[')'] = sfcode[']'] = sfcode['}'] = sfcode['"'] = sfcode['\''] = 0;
    sfcode['.'] = sfcode['!'] = sfcode['?'] = 3000;
    sfcode[';'] = 2000; sfcode[','] = 1100; sfcode_initialized = 1;
    }
  initial_spaces = 1; space_factor = 1000;
  nonglue_characters = glue_l = last_was_glue = 0;
  for (j=0,cp=str; j<str_l; ) {     /* parse string into text and glue chunks */
    int gsize, gstretch, gshrink;
    if (*cp != ' ' && *cp != '[') {                 /* save normal characters */
      str[nonglue_characters++] = *cp; last_was_glue = 0;
      if (space_factor<1000 && sfcode[(unsigned)*cp]>1000) space_factor = 1000;
      else if (sfcode[(unsigned)*cp]) space_factor = sfcode[(unsigned)*cp];
      j++; cp++; initial_spaces = 0;
      }
    else {                                                            /* glue */
      if (*cp == ' ') {                      /* space represent implicit glue */
        j++; cp++; gsize = 10; if (space_factor >= 2000) gsize += 3;
        if (initial_spaces) { gstretch = gshrink = 0; }
        else { gstretch = div_rounded(7*space_factor, 1000);
               gshrink  = div_rounded(3*1000, space_factor); }
        }
      else if (*cp == '[') {     /* explicit glue - parse glue syntax:  [nnn] */
        j++; cp++; gsize = gstretch = gshrink = 0;
        if (j<str_l && isdigit(*cp)) { gsize   = gunits(*cp); j++; cp++; }
        if (j<str_l && isdigit(*cp)) { gstretch= gunits(*cp); j++; cp++; }
        if (j<str_l && isdigit(*cp)) { gshrink = gunits(*cp); j++; cp++; }
        while (j<str_l && *cp!=']') { j++; cp++; }
        if    (j<str_l && *cp==']') { j++; cp++; }
        initial_spaces = 0;
        }
      space_factor = 1000;
      if (glue_l >= MAX_GLUE)/* in emergency treat the rest as a rigid string */
        { str[nonglue_characters++] = ' '; last_was_glue = 0; }
      else {
        if (!last_was_glue) {
          glue_l++; glue_index[glue_l-1] = nonglue_characters;
          glue_size[glue_l-1]=glue_stretch[glue_l-1]=glue_shrink[glue_l-1]=0;
          }
        glue_stretch[glue_l-1] += gstretch; glue_shrink[glue_l-1] += gshrink;
        glue_size[glue_l-1] += gsize; last_was_glue = 1;
        }
      }
    }
  glue_natural_width = 0;
  for (k=0; k<glue_l; k++) glue_natural_width += glue_size[k];
  entire_natural_width = 10*nonglue_characters + glue_natural_width;
  if (width < 0) width = div_rounded(entire_natural_width,10);
  width = min(width,str_m-1);

 /* setting (hardening) glue: calculate final glue_size */
  stretch_by = 10*width - entire_natural_width;        /* 0.1 char size units */
  if (stretch_by > 0) {                                            /* stretch */
    int total_stretch = 0;
    for (k=0; k<glue_l; k++) total_stretch += glue_stretch[k];
    if (total_stretch != 0)
      for (k=0; k<glue_l; k++)
        glue_size[k] += div_rounded(stretch_by*glue_stretch[k], total_stretch);
    }
  else if (stretch_by < 0) {                                        /* shrink */
    int total_shrink = 0;
    for (k=0; k<glue_l; k++) total_shrink += glue_shrink[k];
    if (total_shrink != 0)
      for (k=0; k<glue_l; k++)
        glue_size[k] -= min(glue_shrink[k],
                         div_rounded(-stretch_by*glue_shrink[k], total_shrink));
    }

 /* adjust glue size (0.1 ch units) into an integer number of spaces */
  total_glue_spaces = 0;
  for (k=0; k<glue_l; k++) total_glue_spaces += div_floor(glue_size[k],10);
  while (nonglue_characters+total_glue_spaces < width) {
    int fract, fract_at_best = 0, weight, max_weight = -10, best_ind = -1;
    for (k=0; k<glue_l; k++) {
      if ( (fract = glue_size[k] - 10*div_floor(glue_size[k],10)) != 0) {
        weight = fract + (abs(glue_size[k]) > 30 ? 7 : 0);
        if (weight > max_weight)
          { max_weight = weight; fract_at_best = fract; best_ind = k; }
        }
      }
    if (best_ind < 0) break;
    glue_size[best_ind] += (10-fract_at_best); total_glue_spaces++;
    }
 /* format the string inserting whitespace as calculated */
  { int j, k, dst_l = 0, from = 0;
    int ofs = str_m - nonglue_characters;
    memmove(&str[ofs],str,nonglue_characters);    /* flush string fully right */
    for (k=0; k<glue_l; k++) {                       /* insert glue as spaces */
      while (from < glue_index[k]) str[dst_l++] = str[ofs + from++];
      if (glue_size[k] < 0) dst_l = max(0, dst_l+div_floor(glue_size[k],10));
      else { for (j=div_floor(glue_size[k],10); j>=1; j--) str[dst_l++] = ' '; }
      }
    while (from < nonglue_characters) str[dst_l++] = str[ofs + from++];
    str[min(dst_l,str_m-1)] = '\0';
    return dst_l-width;
  }
}

/* auxiliary routine to make_printable_dsc(); */
/* Saves replacement string in a static array and returns pointer to it. */
char *init_nonprintable_char(str)
  char *str;
{
  static int repl_l = 0;
  static char replacement[300]; /* replacement strings for nonprintable chars */
  char *addr;
  int str_l = strlen(str);
  news_assert(repl_l+str_l+1 < sizeof(replacement));
  strcpy(addr = &replacement[repl_l], str); repl_l += str_l+1;
  return addr;
}

/* auxiliary routine to make_printable_dsc() */
void init_nonprintable(nonprintable)
  char *nonprintable[256];
{
  int nxt;
#define SS(str) nonprintable[nxt++] = init_nonprintable_char(str);

  { int j; for (j=0; j<sizeof(nonprintable); j++) nonprintable[j] = NULL; }
  nxt = 0;
  SS("NUL"); SS("SOH"); SS("STX"); SS("ETX");
  SS("EOT"); SS("ENQ"); SS("ACK"); SS("BEL");
  SS("BS"); nxt++; /*HT*/ SS("LF");  SS("VT");
  SS("FF");  SS("CR");  SS("SO");  SS("SI");
  SS("DLE"); SS("XON"); SS("DC2"); SS("XOF");
  SS("DC4"); SS("NAK"); SS("SYN"); SS("ETB");
  SS("CAN"); SS("EM");  SS("SUB"); SS("ESC");
  SS("FS");  SS("GS");  SS("RS");  SS("US");
  nxt = 127; SS("DEL");
  nxt = 128;
  SS("X80"); SS("X81"); SS("X82"); SS("X83");
  SS("IND"); SS("NEL"); SS("SSA"); SS("ESA");
  SS("HTS"); SS("HTJ"); SS("VTS"); SS("PLD");
  SS("PLU"); SS("RI");  SS("SS2"); SS("SS3");
  SS("DCS"); SS("PU1"); SS("PU2"); SS("STS");
  SS("CCH"); SS("MW");  SS("SPA"); SS("EPA");
  SS("X98"); SS("X99"); SS("X9A"); SS("CSI");
  SS("ST");  SS("OSC"); SS("PM");  SS("APC");
  nxt = 255; SS("XFF");
#undef SAVESTR
}

/*
 *  make_printable_dsc
 *
 *  Returns the address of a statically allocated string descriptor,
 *  pointing to a string which is guaranteed to contain only
 *  printable characters (and TABs).
 *  This may be the original string ('text') or a filtered copy of it
 *  in the statically allocated memory.
 */
struct dsc$descriptor_const_s *make_printable_dsc(text)
  const char *text;
{
  static struct dsc$descriptor_const_s text_dsc =
    {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0};
  static char *nonprintable[256];  /* array of pointers to replacement strings:
                                      NULL if printable, otherwise
                                      pointer to replacement string */
  static int printable_initialized = 0;
  int any_nonprintable, j, text_l, tmp_l, ichr;
  const char *s;
  char tmp[1024];

  if (!printable_initialized)
    { init_nonprintable(nonprintable); printable_initialized = 1; }
  /* first make a quick check for non-printable characters;
     if all goes well, we can use the original text unmodified
     and save us some work */
  any_nonprintable = 0;
  for (s=text, text_l=0; *s; text_l++)
    if (nonprintable[(unsigned char) *(s++)]) any_nonprintable = 1;
  if (!any_nonprintable)
    { text_dsc.dsc$a_pointer = text; text_dsc.dsc$w_length = text_l; }
  else {
    tmp_l = 0;
    for (s=text; (ichr=(unsigned char)*s); s++) {
      if (!nonprintable[ichr]) {
        if (tmp_l >= sizeof(tmp)) break;
        tmp[tmp_l++] = (char) ichr;
        }
      else {
        j = strlen(nonprintable[ichr]);
        if (tmp_l+2+j > sizeof(tmp)) break;
        tmp[tmp_l++] = '<';
        strcpy(&tmp[tmp_l],nonprintable[ichr]);  tmp_l += j;
        tmp[tmp_l++] = '>';
        }
      }
    text_dsc.dsc$a_pointer = tmp; text_dsc.dsc$w_length = tmp_l;
    }
  return &text_dsc;
}

/*
 * Calls SMG$PUT_CHARS, simplifying parameter passing
 * and taking care that nonprintable characters are not passed to SMG.
 *
 * Numeric parameters are passed by value  (not by ref as for SMG routine),
 * ASCIZ string is passed by reference (not by descriptor).
 */
void smg_put_chars(vdispl_id,text,row,column,flags,rendition)
  int vdispl_id, row, column, flags, rendition;
  const char *text;
{
  int status;
  _c$cks(smg$put_chars(&vdispl_id,make_printable_dsc(text),
                       &row,&column,&flags,&rendition,0,0));
}


/*
 *  clear_err_line
 *
 *  error line management
 */

void clear_err_line()
{
  int status;
  if (smg_active) _c$cks(smg$erase_line(&trailer_vd,c$ac(3),c$ac(1)));
}


/*
 *  create_keydefs
 *
 *  Create key definitions
 */

void create_keydefs()
{

#define ds(s) c$dsc(s)
#define term c$ac(SMG$M_KEY_TERMINATE)
#define tmne c$ac(SMG$M_KEY_TERMINATE+SMG$M_KEY_NOECHO)
#define gold ds("GOLD")
#define akd(b,c,d,e,f) _c$cks(smg$add_key_def(&keytab,b,c,d,e,f))

  int status;
  unsigned char kit[SMG$C_KEYBOARD_INFO_BLOCK];
#define SMG_B_DEVCLASS 12	/* 1st byte of 4th longword; skip 3*4 bytes */

  _c$cks(smg$create_virtual_keyboard(&kid,0,0,0));
  /* Note: the VMS documenation (V5.x version, at any rate) for the keyboard
     information table argument is wrong.  The argument should be a pointer
     to the block which is supplied by the caller and written by SMG, not a
     pointer to a pointer to the block as the SMG manual claims.	[pr] */
  _c$cks(smg$get_keyboard_attributes(&kid,kit,
                                     c$ac(SMG$C_KEYBOARD_INFO_BLOCK)));
  session_is_interactive = (kit[SMG_B_DEVCLASS] == DC$_TERM);
  _c$cks(smg$create_key_table(&keytab));
  akd(ds("PF1"),0,0,0,gold);
  akd(ds("PF2"),0,term,ds("HELP"),0);
  akd(ds("PF3"),0,term,ds("SKIP"),0);
  akd(ds("PF4"),gold,term,ds("SKIP/FOLLOWUP"),0);
  akd(ds("PF4"),0,term,ds("SKIP/NEWSGROUP"),0);
  akd(ds("KP0"),0,term,ds("READ/NEW"),0);
  akd(ds("KP0"),gold,term,ds("READ/HEADER/NEW"),0);
  akd(ds("KP1"),0,c$ac(0),ds("READ "),0);
  akd(ds("KP1"),gold,c$ac(0),ds("READ/HEADER "),0);
  akd(ds("KP2"),0,tmne,ds("DOWN"),0);
  akd(ds("KP2"),gold,tmne,ds("BOTTOM"),0);
  akd(ds("KP3"),0,term,ds("READ/NEXT"),0);
  akd(ds("KP3"),gold,term,ds("READ/NEXT/HEADER"),0);
  akd(ds("KP4"),0,term,ds("DIR"),0);
  akd(ds("KP5"),0,tmne,ds("UP"),0);
  akd(ds("KP5"),gold,tmne,ds("TOP"),0);
  akd(ds("KP6"),0,term,ds("DIR/REG"),0);
  akd(ds("KP7"),0,term,ds("DIR"),0);
  akd(ds("KP7"),gold,term,ds("PRINT"),0);
  akd(ds("KP8"),0,term,ds("DIR/NEW"),0);
  akd(ds("KP8"),gold,c$ac(0),ds("EXTRACT "),0);
  akd(ds("KP9"),0,term,ds("DIR/REG"),0);
  akd(ds("KP9"),gold,c$ac(0),ds("EXTRACT/ALL "),0);
  akd(ds("MINUS"),0,term,ds("DIR/ALL"),0);
  akd(ds("MINUS"),gold,c$ac(0),ds("EXTRACT/APPEND "),0);
  akd(ds("COMMA"),0,term,ds("DIR/ALL"),0);
  akd(ds("COMMA"),gold,term,ds("SELECT/NEW"),0);
  akd(ds("PERIOD"),0,term,ds("READ/NEW/FOLLOWUP"),0);
  akd(ds("PERIOD"),gold,term,ds("READ/NEW/FOLLOWUP/HEADER"),0);
  akd(ds("HELP"),0,term,ds("HELP"),0);
  akd(ds("FIND"),0,c$ac(0),ds("SEARCH "),0);
  akd(ds("INSERT_HERE"),0,term,ds("REGISTER"),0);
  akd(ds("REMOVE"),0,term,ds("DEREGISTER"),0);
  akd(ds("SELECT"),0,term,ds("SELECT"),0);
  akd(ds("SELECT"),gold,term,ds("CLOSE"),0);
  akd(ds("PREV_SCREEN"),0,tmne,ds("SCUP"),0);
  akd(ds("PREV_SCREEN"),gold,tmne,ds("TOP"),0);
  akd(ds("NEXT_SCREEN"),0,tmne,ds("SCDOWN"),0);
  akd(ds("NEXT_SCREEN"),gold,tmne,ds("BOTTOM"),0);
  akd(ds("CTRLW"),0,tmne,ds("REFRESH"),0);
  akd(ds("LEFT"),0,term,ds("CLOSE"),0);
  akd(ds("RIGHT"),0,term,ds("OPEN"),0);
  akd(ds("F10"),0,term,ds("EXIT"),0);
#if MENU_SUPPORT
  akd(ds("F11"),0,term,ds("Menu"),0);
#endif
  if (keytab) {
    status = smg$load_key_defs(&keytab, c$dsc(News_ini),
                               c$dsc("SYS$LOGIN:NEWS_INI."), c$ac(0));
    if ((status & 1) || (status == RMS$_FNF)) ;
    else report_rms_error_routine(NEWS$_LOADKEYFAIL,status,0,module_name_str,__LINE__);
    }
  c$free_tmp();
#undef ds
#undef term
#undef tmne
#undef gold
#undef akd
}

/*
 *  cur_down_grp
 *
 *  Move the group cursor down by 1 newsgroup - (set the level to 1!)
 */

int cur_down_grp(n,refresh)
  int n, refresh;
{
  int ng,
      png = curr_g,
      ret_val = 0;

  set_level(1);
  if (n <= 0) return(0);

  if (!curr_g) return(0);
  if (ga[curr_g]->grp_display_indx) {
    for (ng = curr_g+1; ng <= ga_size; ++ng) {
      if (ga[ng]->grp_display_indx) {
        png = ng;
        ++ret_val;
        if (!--n) break;
        }
      }
    curr_g = png;
    if (smg_active) {
      if (g_arrow) smg_put_chars(grp_vd,"  ",g_arrow,1,0,0);
      g_arrow = ga[curr_g]->grp_display_indx;
      smg_put_chars(grp_vd,"->",g_arrow,1,0,SMG$M_REVERSE+SMG$M_BOLD);
      position_display(&grp_vd,&grp_paste,g_arrow,refresh,grp_display_size);
      }
    return(ret_val);
    }
  return(0);
}

/*
 *  cur_down_itm
 *
 *  Move the item cursor down by n items
 */

int cur_down_itm(g,n,refresh)		int g,n,refresh;
{
  int ng,
      png,
      ret_val = 0;

  if (n <= 0) return(0);

  if ((g) && (ga[g]->grp_ia)) {
    if (!(png = ga[g]->grp_c_itm)) return(0);
    for (ng = ga[g]->grp_c_itm+1; ng <= ga[g]->grp_count; ++ng) {
      png = ng;
      ++ret_val;
      if (!--n) break;
      }
    if ((smg_active) && (ga[g]->grp_iavdsize)) {
      if (ga[g]->grp_c_itm)
        smg_put_chars(ga[g]->grp_iavd,"  ",ga[g]->grp_c_itm,1,0,0);
      smg_put_chars(ga[g]->grp_iavd,"->",png,1,0,SMG$M_REVERSE+SMG$M_BOLD);
      position_display(&ga[g]->grp_iavd,&ga[g]->grp_iapaste,png,refresh,ga[g]->grp_count);
      }
    ga[g]->grp_c_itm = png;
    if ((curr_g == g) && (news_context > 1)) {
      curr_i = png;
      set_level(2);
      }
    return(ret_val);
    }
  return(0);
}

/*
 *  cur_set_grp
 *
 *  set the group pointer to g
 */

void
cur_set_grp(g)
    int g;
{
  curr_g = g;
  if (smg_active) {
    if ((curr_g) && (g_arrow == ga[curr_g]->grp_display_indx)) {
      position_display(&grp_vd,&grp_paste,g_arrow,0,grp_display_size);
      return;
      }
    if (g_arrow) smg_put_chars(grp_vd,"  ",g_arrow,1,0,0);
    if (curr_g) {
      g_arrow = ga[curr_g]->grp_display_indx;
      if (g_arrow) {
        smg_put_chars(grp_vd,"->",g_arrow,1,0,SMG$M_REVERSE+SMG$M_BOLD);
        position_display(&grp_vd,&grp_paste,g_arrow,0,grp_display_size);
        }
      }
    else g_arrow = 0;
    }
}

/*
 *  cur_set_itm
 *
 *  set the item pointer to item number i
 */

void cur_set_itm(g,i)
  int g,i;
{
  if (i < ga[g]->grp_c_itm) cur_up_itm(g,ga[g]->grp_c_itm - i,0);
  else if (i > ga[g]->grp_c_itm) cur_down_itm(g,i - ga[g]->grp_c_itm,0);
  else {
    if ((smg_active) && (ga[g]->grp_iavdsize)) {
      smg_put_chars(ga[g]->grp_iavd,"->",ga[g]->grp_c_itm,1,0,SMG$M_REVERSE+SMG$M_BOLD);
      position_display(&ga[g]->grp_iavd,&ga[g]->grp_iapaste,ga[g]->grp_c_itm,0,ga[g]->grp_count);
      }
    if ((curr_g == g) && (news_context > 1)) {
      curr_i = ga[g]->grp_c_itm;
      set_level(2);
      }
    }
}

/*
 *  cur_up_grp
 *
 *  Move the group cursor down by 1 newsgroup - (set the level to 1!)
 */

int
cur_up_grp(n,refresh)
  int n,refresh;
{
  int ng,
      png = curr_g,
      ret_val = 0;

  set_level(1);
  if (n <= 0) return(0);

  if (!curr_g) return(0);
  if (ga[curr_g]->grp_display_indx) {
    for (ng = curr_g-1; ng > 0 ; --ng) {
      if (ga[ng]->grp_display_indx) {
        png = ng;
        ++ret_val;
        if (!--n) break;
        }
      }
    curr_g = png;
    if (smg_active) {
      if (g_arrow) smg_put_chars(grp_vd,"  ",g_arrow,1,0,0);
      g_arrow = ga[curr_g]->grp_display_indx;
      smg_put_chars(grp_vd,"->",g_arrow,1,0,SMG$M_REVERSE+SMG$M_BOLD);
      position_display(&grp_vd,&grp_paste,g_arrow,refresh,grp_display_size);
      }
    return(ret_val);
    }
  return(0);
}

/*
 *  cur_up_itm
 *
 *  Move the item cursor up by n items
 */

int
cur_up_itm(g,n,refresh)
  int g,n,refresh;
{
  int ng,
      png,
      ret_val = 0;

  if (n <= 0) return(0);

  if ((g) && (ga[g]->grp_ia) && (ga[g]->grp_count)) {
    png = ga[g]->grp_c_itm;
    for (ng = ga[g]->grp_c_itm-1; ng > 0; --ng) {
      png = ng;
      ++ret_val;
      if (!--n) break;
      }
    if ((smg_active) && (ga[g]->grp_iavdsize)) {
      if (ga[g]->grp_c_itm)
        smg_put_chars(ga[g]->grp_iavd,"  ",ga[g]->grp_c_itm,1,0,0);
      smg_put_chars(ga[g]->grp_iavd,"->",png,1,0,SMG$M_REVERSE+SMG$M_BOLD);
      position_display(&ga[g]->grp_iavd,&ga[g]->grp_iapaste,png,refresh,ga[g]->grp_count);
      }
    ga[g]->grp_c_itm = png;
    if ((curr_g == g) && (news_context > 1)) {
      curr_i = png;
      set_level(2);
      }
    return(ret_val);
    }
  return(0);
}

/*
 *  do_newg
 *
 *  Create newsgroup if it does not already exist.
 */

unsigned int do_newg(g,prompt,use_sysfilter)
  char *g;
  int prompt, use_sysfilter;
{
  int status;
  GRP savegrp;
  int new_mem = 0;
  char s[SUBJLEN];

  if ((!g) || (!*g)) return(0);

  util_cvrt(s,g);
  grprab.rab$l_kbf = s;
  grprab.rab$b_ksz = SUBJLEN;
  grprab.rab$b_krf = 0;
  grprab.rab$l_rop = RAB$M_RRL | RAB$M_NLK;
  grprab.rab$b_rac = RAB$C_KEY;

  if (!sys_get_nornf(&grprab)) {
    int acpt;

    if (no_priv()) return(0);
    if (!auto_cre_grp) return(0);

    acpt = sys_accept_group(s);

    if (prompt || (!acpt && use_sysfilter)) {
      sprintf(err_oline,"Newsgroup: %s",s);
      if (!acpt) strcat(err_oline," (NEWS.SYS Filter rejects newsgroup name)");
      err_line(err_oline);
      status = get_input(&usr_inp_dsc,c$dsc("Create Newsgroup? [n]:"),&usr_inp_l);
      if ((!(status & 1)) || (!usr_inp_l)) return(0);
      if (toupper(*usr_inp) != 'Y') return(0);
      }
/*  else if (!acpt && use_sysfilter) return(0);  RRS */

    sprintf(itm_fname,Grp_template,util_dir(s));
    sysprv();
    if (!(lib$create_dir(c$dsc(itm_fname),0,0,0,0,0) & 1)) return(0);
    nosysprv();
    new_mem = 1;
    savegrp = newsgrp;
    grprab.rab$l_kbf = (char *) c$rfi(0);
    grprab.rab$b_ksz = 4;
    grprab.rab$b_krf = 1;
    grprab.rab$l_rop = RAB$M_WAT;
    grprab.rab$b_rac = RAB$C_KEY;
    sys_get(&grprab);

    ++newsgrp.grp_count;
    time((time_t *) &newsgrp.grp_entdate);
    savegrp.grp_num = ++newsgrp.grp_topnum;
    savegrp.grp_srvcache = newsgrp.grp_srvcache;
    savegrp.grp_srvproto = newsgrp.grp_srvproto;
    savegrp.grp_flags = newsgrp.grp_flags;
/*
    savegrp.grp_flags &= ~NEWS_M_NNTPCACHE;
    savegrp.grp_flags |= newsgrp.grp_flags & NEWS_M_NNTPCACHE;
*/
    strcpy(savegrp.grp_srvnode,newsgrp.grp_srvnode);
    sys_update(&grprab);

    newsgrp = savegrp;
    memset(((char *) &newsgrp) + NEWS_GRPFIL_RSZ, 0,
           sizeof newsgrp - NEWS_GRPFIL_RSZ);
    strncpy(newsgrp.grp_name,s,SUBJLEN);
    newsgrp.grp_topnum = 0;
    newsgrp.grp_firstnum = 1;
    newsgrp.grp_count = 0;
    time((time_t *) &newsgrp.grp_credate);
    time((time_t *) &newsgrp.grp_entdate);
    newsgrp.grp_life = newsgrp.grp_itmlife = 0;
    newsgrp.grp_reg = newsgrp.grp_unread = 0;
    newsgrp.grp_ia = (ITM_PTR) NULL;
    newsgrp.grp_iasize = 0;
    newsgrp.grp_iavd = 0;
    newsgrp.grp_display_indx = newsgrp.grp_iavdsize = newsgrp.grp_iapaste = 0;
    newsgrp.grp_flags = newsgrp.grp_c_itm = 0;
    newsgrp.grp_reg_text = (char *)0;
    newsgrp.grp_topic[0] = newsgrp.grp_notice[0] = '\0';
    grprab.rab$w_rsz = NEWS_GRPFIL_RSZ;
    sys_put(&grprab);
    }
  else if (!ga_search_name(s)) new_mem = 1;
  if (new_mem) do_new_mem_grp();
  return(newsgrp.grp_num);
}

/*
 *  do_new_group
 *
 *  Create newsgroup if it does not already exist.
 */

void do_new_group(g,prompt,retg)
  const char *g;
  int prompt;
  unsigned int *retg;
{
  char *locg,
       *cp1,
       *cp2;

  strcpy(cp1=locg=(char *)news_malloc(strlen(g)+1),g);
  *retg = 0;
  while ((cp2 = strchr(cp1,',')) != 0) {
    *cp2++ = '\0';
    if ((*retg = do_newg(cp1,prompt,1)) != 0) *++retg = 0;
    cp1 = cp2;
    }
  if ((*retg = do_newg(cp1,prompt,1)) != 0) *++retg = 0;
}

void create_newsgroups(g,retg)
  char *g;
  unsigned int *retg;
{
  char *locg, *cp1, *cp2;
  int prompt = 0, sacg = auto_cre_grp;

  auto_cre_grp = 1;
  strcpy((locg = (char *)news_malloc(strlen(g) + 1)),g);
  cp1 = locg;
  *retg = 0;
  while ((cp2 = strchr(cp1,',')) != 0) {
    *cp2++ = '\0';
    if ((*retg = do_newg(cp1,prompt,0)) != 0) *++retg = 0;
    cp1 = cp2;
    }
  if ((*retg = do_newg(cp1,prompt,0)) != 0) *++retg = 0;
  news_free(locg);
  auto_cre_grp = sacg;
}

void load_newsgroups(g,retg)
  char *g;
  unsigned int *retg;
{
  char *locg, *cp1, *cp2;
  int prompt = 0;

  strcpy((locg = (char *)news_malloc(strlen(g) + 1)),g);
  cp1 = locg;
  *retg = 0;
  while ((cp2 = strchr(cp1,',')) != 0) {
    *cp2++ = '\0';
    if ((*retg = do_newg(cp1,prompt,1)) != 0) *++retg = 0;
    cp1 = cp2;
    }
  if ((*retg = do_newg(cp1,prompt,1)) != 0) *++retg = 0;
  news_free(locg);
}

/*
 *  do_openi
 *
 *  Given an item record, open the associated text of the message.
 *  Use SYSPRV if WRITE access required, and change the mask to (g:re,w:re).
 */

FILE *do_openi(mode,server_name,protected,flk)
  const char *mode;
  char *server_name;
  int protected, *flk;
{
  FILE *tmp_fpr;
  int nmask = 022, omask = 0;
  struct stat sb;

  if (flk) *flk = 0;
  if (protected) nmask = 077;
  if (*mode == 'w')
    omask = umask(nmask);

  sysprv();

  if (server_name) *server_name = '\0';
  if(*mode == 'r')
       tmp_fpr = fopen(itm_fname, "r", "mbc=16");
  else tmp_fpr = fopen(itm_fname,mode,"mbc=16", "rat=cr","rfm=var");

  if (tmp_fpr && !ferror(tmp_fpr)) ;   /* ok */
  else if (flk && errno == EVMSERR && vaxc$errno == RMS$_FLK) {
    tmp_fpr = NULL;
    *flk =1;
    }
  else {
    tmp_fpr = NULL;
    if (errno == ENOENT) ;              /* expected error - file not found */
    else if (*mode == 'r') _ck_open_r(tmp_fpr,itm_fname);
         else              _ck_open_w(tmp_fpr,itm_fname);
    }

  if (tmp_fpr && (server_name))
    *server_name = 1;

  if (tmp_fpr && *mode == 'r')  /*RRS if we opened the file for reading OK */
    if (!fstat(fileno(tmp_fpr),&sb) && sb.st_size == 0) { /*RRS null file? */
      fclose(tmp_fpr);                     /*RRS pretend it wasn't there   */
      tmp_fpr = 0;
      }

  if (*mode == 'w') umask(omask);

  nosysprv();

  return(tmp_fpr);
}

/*
 *  do_oitem
 *
 *  Given an item record, open the associated text of the message.
 *  Use SYSPRV if WRITE access required, and change the mask to (g:re,w:re).
 */

FILE *do_oitem(itm,mode,grp_name,server_name,protected,flk)
  const ITM *itm;
  const char *mode, *grp_name;
  char *server_name;
  int protected, *flk;
{
  sprintf(itm_fname,Itm_template,util_dir(grp_name),itm->itm_num);
  return(do_openi(mode,server_name,protected,flk));
}

/*
 *  do_new_item
 *
 *  Create new item in newsgroup
 */

static
char nst[] = "";

unsigned int do_new_item(g,id,subj,fromstr,fnam,new_flag,
                         skip_history,linecount,bypass_signalling_errors)
  unsigned int *g;
  char *id, *subj, *fnam, *fromstr;
  int new_flag, skip_history, linecount, bypass_signalling_errors;
{
  ITM savitm;
  struct stat sbuffer;
  unsigned int cre_grp[100], cre_itm[100];
  time_t cur_time;
  int ga_indx, i, g_count = 0;
  struct rms_file *ofile = 0, *ifile = 0;
  unsigned int *gptr;
  GRP_PTR gap = 0;
  char ibuf[10240], mod[255], id_key[IDLEN + 4];
  int status;
  static char *xrefline = NULL;
  static int xrefline_s = 0;  /* allocated size of xrefline */
  static int xrefline_l = 0;  /* length of string in xrefline */
  extern int rms$errno;

  status = 1;
  *mod = '\0';
  xrefline_l = 0;
  if (fnam && !*fnam) {
    strcpy(no_new_item,"No input filename specified");
    return(0x10000000);
    }

  if ((!id) || (!*id)) {
    strcpy(no_new_item,"No message identifier string given");
    return(0x20000000);
    }

  if (!*g) {
    strcpy(no_new_item,"No newsgroup index value specified");
    return(0x3000000);
    }
  if (fnam) {
    sysprv();
    if (stat(fnam,&sbuffer)) {
      strcpy(no_new_item,"Cannot access input file");
      nosysprv();
      return(0x40000000);
      }

    if (!sbuffer.st_size) {
      nosysprv();
      strcpy(no_new_item,"Input file is empty");
      return(0x50000000);
      }

    if (!(ifile = rms_open(fnam,0,bypass_signalling_errors))) {
      strcpy(no_new_item,"Cannot open input file (read access)");
      nosysprv();
      return(0x60000000);
      }
    nosysprv();
    }
  util_idcpy(id_key,id);

  if ((!skip_history) && (hist_check(id_key))) {
    strcpy(no_new_item,"Not added (found in history)");
    if (ifile) rms_close(ifile);
    return(0xb0010001U);
    }

  if (!subj) subj = nst;

  if (!xrefline) xrefline = (char *) news_malloc(xrefline_s = 512);
  sprintf(xrefline,"Xref: %s",news_node);
  xrefline_l = strlen(xrefline);

  time(&cur_time);
  newsitm.itm_recvdate = cur_time;
  newsitm.itm_flags = (new_flag ? NEWS_M_NEW : 0) | NEWS_M_UNREAD | NEWS_M_LINESVALID;
  newsitm.itm_lines = linecount;
  newsitm.itm_life = mail_add_expiry;
  newsitm.itm_cachedate = cur_time;
  util_subjcpy(newsitm.itm_title,subj);
  util_idcpy(newsitm.itm_id,id);
  util_fromcpy(newsitm.itm_from,fromstr);

  itmrab.rab$b_krf = 1;
  itmrab.rab$l_kbf = id_key;
  itmrab.rab$b_ksz = IDLEN + 4;
  itmrab.rab$l_rop = RAB$M_WAT;
  itmrab.rab$b_rac = RAB$C_KEY;
  itmrab.rab$w_rsz = sizeof newsitm;

  grprab.rab$b_ksz = 4;
  grprab.rab$b_krf = 1;
  grprab.rab$l_rop = RAB$M_WAT;
  grprab.rab$b_rac = RAB$C_KEY;

  for (; *g ; ++g) {
    grprab.rab$l_kbf = (char *) g;

    if (!sys_get_nornf(&grprab)) {
      status &= 0x0fffffff;
      status |= 0x70000000;
      strcpy(no_new_item,"Newsgroup not located");
      continue;
      }

    if (newsgrp.grp_flags & NEWS_M_MAILGROUP) {
      sys_free_nornl(&grprab);
      status = 2;
      forward_posting = 0;
      strcpy(no_new_item,"Attempt to add item to Mail newsgroup\n");
      continue;
      }
    else if ((newsgrp.grp_flags & NEWS_M_MAILLIST) && !net_news) {
      sys_free_nornl(&grprab);
      status = 2;
      forward_posting = 0;
      strcpy(no_new_item,"Attempt to add item to MailList newsgroup\n");
      continue;
      }
    else if (newsgrp.grp_flags & NEWS_M_MAILMODERATE) {
      if (!net_news) {
        sprintf(mod,"%s@%s",usr_username,Node_address);
        forward_posting = 0;
        if (!strcmp(mod,moderator_address(newsgrp.grp_name))) forward_posting = 1;
        else if (!itm_approved) {
          if (ifile) rms_close(ifile);
          if (!fnam) fnam = create_article(NULL,NULL);
          if (fnam) {
            call_mail(fnam,subj,add_transform(moderator_address(newsgrp.grp_name)),
                      "Post to newsgroup Moderator");
            sysprv();
            ifile = rms_open(fnam,0,bypass_signalling_errors);
            nosysprv();
            sys_free_nornl(&grprab);
            status = 2;
            strcpy(no_new_item,"Non-Approved posting to Moderated Newsgroup");
            }
          else {
            sys_free_nornl(&grprab);
            strcpy(no_new_item,"Cannot access input file");
            status = 0x40000000;
            }
          continue;
          }
        }
      else {
        if (!(newsgrp.grp_flags & NEWS_M_NOAPPROVAL) && !itm_approved) {

#if UNAPPROVED_NEWS == MAIL_TO_MODERATOR
          if (ifile) rms_close(ifile);
          if (!fnam) fnam = create_article(NULL,NULL);
          if (fnam) {
            call_mail(fnam,subj,add_transform(moderator_address(newsgrp.grp_name)),
                      "Post to newsgroup Moderator");
            sysprv();
            ifile = rms_open(fnam,0,bypass_signalling_errors);
            nosysprv();
            }

#elif UNAPPROVED_NEWS == DISCARD
          sys_free_nornl(&grprab);
          status = 2;
          strcpy(no_new_item,"Non-Approved posting to Moderated Newsgroup");
          continue;
#endif

          }
        }
      }

    if ((ga_indx = ga_locate(*g)) > 0) gap = ga[ga_indx];
    else {
      do_new_mem_grp();
      if (!(ga_indx = ga_locate(*g))) {
        sys_free_nornl(&grprab);
        status = 2;
        strcpy(no_new_item,"Newsgroup not located (in local store)");
        continue;
        }
      gap = ga[ga_indx];
      }

    newsitm.itm_grp = newsgrp.grp_num;
    gptr = (unsigned int *) &id_key[IDLEN];
    *gptr = newsitm.itm_grp;

    savitm = newsitm;

    if (sys_get_nornf(&itmrab)) {	/* #3 glass@vixvax.mgi.com */
      if (!newsitm.itm_cachedate) {
        time((time_t *) &newsitm.itm_cachedate);
        sys_update(&itmrab);
        savitm.itm_num = newsitm.itm_num;
        newsitm = savitm;
        }
      else {
        status &= 0x0fffffff;
        status |= 0xB0000000U;
        strcpy(no_new_item,"Not added (already in NEWS.ITEMS file)");
        newsitm = savitm;
        continue;
        }
      }
    else {
      newsitm = savitm;
      ++newsgrp.grp_count;
      newsgrp.grp_entdate = cur_time;
      newsitm.itm_num = ++newsgrp.grp_topnum;
      newsitm.itm_cid = newsitm.itm_num;
      itmrab.rab$l_rbf = (char *) &newsitm;
      itmrab.rab$w_rsz = sizeof newsitm;

      if (newsgrp.grp_flags & NEWS_M_IGNEXP) newsitm.itm_life = 0;

#if ITMLIFE_DEFAULTS
      if (newsitm.itm_life) {
	if (newsgrp.grp_itmlife)
	  newsitm.itm_life = min(newsitm.itm_life,newsgrp.grp_itmlife);
	else if (ga[0]->grp_itmlife)
	  newsitm.itm_life = min(newsitm.itm_life,ga[0]->grp_itmlife);
	else
	  newsitm.itm_life = min(newsitm.itm_life,EXP_TIME);
	}
#endif

      if (!sys_put_nodup(&itmrab)) {
        int ok = 0;

        itmrab.rab$b_krf = 0;
        itmrab.rab$l_kbf = (char *) &(newsitm.itm_num);
        itmrab.rab$b_ksz = 8;
        if (sys_find_nornf(&itmrab)) {
          do { newsitm.itm_num = ++newsgrp.grp_topnum;
             } while (sys_find_nornf(&itmrab));
          itmrab.rab$l_rbf = (char *) &newsitm;
          itmrab.rab$w_rsz = sizeof newsitm;
          if (sys_put_nodup(&itmrab)) ok = 1;
          }
        itmrab.rab$b_krf = 1;
        itmrab.rab$l_kbf = id_key;
        itmrab.rab$b_ksz = IDLEN + 4;
        itmrab.rab$l_rop = RAB$M_WAT;
        itmrab.rab$b_rac = RAB$C_KEY;
        itmrab.rab$w_rsz = sizeof newsitm;
        if (!ok) {
          status &= 0x0fffffff;
          status |= 0xB0000000U;
          strcpy(no_new_item,"Cannot add to Newsitem index file (case C)");
          continue;
          }
        }

#if ITMLIFE_DEFAULTS
      newsitm.itm_life = mail_add_expiry;
#endif

      if (newsgrp.grp_flags & NEWS_M_IGNEXP) newsitm.itm_life = mail_add_expiry;

      if (!sys_update(&grprab)) {
        itmrab.rab$l_kbf = (char *) &(newsitm.itm_num);
        itmrab.rab$b_ksz = 8;
        itmrab.rab$b_krf = 0;
        itmrab.rab$l_rop = RAB$M_WAT;
        itmrab.rab$b_rac = RAB$C_KEY;
        if (sys_find_nornf(&itmrab)) sys_delete(&itmrab);

        status &= 0x0fffffff;
        status |= 0xA0000000U;
        strcpy(no_new_item,"Cannot update Newsgroup index file");
        continue;
        }
      gap->grp_topnum = newsgrp.grp_topnum;
      savitm.itm_num = newsitm.itm_num;
      do_new_mem_itm(ga_indx,gap);
      newsitm.itm_num = savitm.itm_num;
      }
    cre_itm[g_count] = newsitm.itm_num;
    cre_grp[g_count++] = ga_indx;
    if (xrefline_s - xrefline_l < 256) {
      xrefline = (char *) news_realloc(xrefline, xrefline_s += 256);
      }
    sprintf(&xrefline[xrefline_l]," %s:%d",gap->grp_name,newsitm.itm_num);
    xrefline_l += strlen(&xrefline[xrefline_l]);
    }

  *itm_fname = '\0';
  if (!g_count) {
    if (ifile) rms_close(ifile);
    return((!status) ? (strcpy(no_new_item,"No Valid Newsgroups"),status = 1) : status);
    }

  for (i = 0 ; i < g_count; ++i) {
    forward_posting = 1;
    if (!*itm_fname) {
      int header = 1;

      sprintf(itm_fname,Itm_template,util_dir(ga[cre_grp[i]]->grp_name),cre_itm[i]);
      if (ifile) {
        if (!(ofile = rms_create(itm_fname,0xa000,bypass_signalling_errors))) {
          sprintf(no_new_item,"Cannot open output file (write access - %s/0x%x)",
		strerror(errno,vaxc$errno),rms$errno);
          status = 0x90000000U;
          *itm_fname = '\0';
          continue;
          }
        while (rms_get(ibuf,sizeof(ibuf)-1,ifile)) {
          if (header && (!*ibuf)) {
            if ((g_count > 1) && (header)) rms_put(xrefline,ofile);
            rms_put(ibuf,ofile);
            header = 0;
            }
          else if (header && (!strncmp(ibuf,"Xref:",5))) {
            if (g_count > 1) rms_put(xrefline,ofile);
            header = 0;
            }
          else rms_put(ibuf,ofile);
          }
        rms_close(ifile); ifile = 0;
        rms_close(ofile);
        }
      else {
        if (create_article(itm_fname,(g_count > 1 ? xrefline : NULL)) == NULL) {
          sprintf(no_new_item,"Cannot open output file (write access - %s/0x%x)",
		strerror(errno,vaxc$errno),rms$errno);
          status = 0x90000000U;
          continue;
          }
        }
      if (ga[cre_grp[i]]->grp_flags & NEWS_M_RESTRICT_SET) {
        sysprv();
        chmod(itm_fname,0700);
        nosysprv();
        }
      status = 0;
      strcpy(no_new_item,"Newsitem added");
      no_more_news = 0;
      }
    else {
      char fline[256];

      sprintf(fline,Itm_template,util_dir(ga[cre_grp[i]]->grp_name),cre_itm[i]);
      sysprv();
      file_copy(itm_fname,fline,bypass_signalling_errors);
      if (ga[cre_grp[i]]->grp_flags & NEWS_M_RESTRICT_SET) chmod(fline,0700);
      nosysprv();
      status = 0;
      strcpy(no_new_item,"Newsitem added");
      no_more_news = 0;
      }
    }
  if (ifile) rms_close(ifile);
  return(status);
}

/*
 *  do_new_mem_grp
 *
 *  create a new group in the mem array
 */

void do_new_mem_grp()
{
  GRP_PTR nentry;
  int status;
  int i,
      j;

  ++(ga[0]->grp_count);
  nentry = (GRP_PTR) news_malloc(sizeof newsgrp);
  *nentry = newsgrp;
  if (reorder_groups) i = ga_size + 1;
  else for (i = 1; i <= ga_size; ++i) if (strcmp(ga[i]->grp_name,newsgrp.grp_name) > 0) break;
  if (ga_malloc <= (ga_size + 2)) {
    ga_malloc += 4;
    ga = (GRP_PTR *) news_realloc(ga,ga_malloc * (sizeof nentry));
    }
  if (i <= ga_size) for (j = ga_size; j >= i; --j) ga[j+1] = ga[j];
  if (curr_g >= i) ++curr_g;
  ga[i] = nentry;
  ga[i]->grp_reg = NEWGROUP_REG | (1 & (profile_flags & PROFILE_NEWREGISTER));
  ga[i]->grp_display_indx = (cur_dir_type < DIR_NEW);
  ++ga_size;

  if ((gv_size) && (ga[0]->grp_count > gv_size))
    _c$cks(smg$change_virtual_display(&grp_vd,c$rfi(gv_size += 4),c$ac(devcol),0,0,0));
  if ((smg_active) && (cur_dir_type < DIR_NEW)) screen_map_dir();
}

/*
 *  gendate
 *
 *  generate a reasonable date string!!
 */

static
char fmt_date[26];

char *gendate(i)
  unsigned int i;
{
  if (!i) fmt_date[1] = '\0';
  else {
    strncpy(fmt_date,ctime((time_t *)&i),24);
    fmt_date[1]=fmt_date[8];fmt_date[2]=fmt_date[9];fmt_date[3]='-';
    fmt_date[7]='-';fmt_date[8]=fmt_date[22];fmt_date[9]=fmt_date[23];
    fmt_date[10]='\0';
    }
  return(&fmt_date[1]);
}

/*
 *  gen_id
 *
 *  generate a unique message id of the form <seq num>@<internet address>
 */
char genid[132];

char *gen_id()
{
  GRP savegrp;
  time_t cur_time;
  char *timestr, *cp;

  savegrp = newsgrp;

  time(&cur_time);
  timestr = ctime(&cur_time);

  if (nntp_client) {
    sprintf(genid,"<%.4s%.3s%.2s.%.2s%.2s%.2s@%s>",
      &timestr[20],&timestr[4],&timestr[8],
      &timestr[11],&timestr[14],&timestr[17],
      news_node);
    }
  else {
#if !NNTP_CLIENT_ONLY
    int status;
    int seq = 1;

    grprab.rab$l_kbf = (char *) c$rfi(0);
    grprab.rab$b_ksz = 4;
    grprab.rab$b_krf = 1;
    grprab.rab$l_rop = RAB$M_WAT;
    grprab.rab$b_rac = RAB$C_KEY;
    if (sys_get_nornf(&grprab)) {
      seq = ++newsgrp.grp_iavd;
      sys_update(&grprab);
      }
    sprintf(genid,"<%.4s%.3s%.2s.%.2s%.2s%.2s.%d@%s>",
      &timestr[20],&timestr[4],&timestr[8],
      &timestr[11],&timestr[14],&timestr[17],
      seq, news_node);
#endif
    }

  cp = &genid[8];
  if (*cp == ' ') {
    do *cp = *(cp + 1); while (*cp++);
    }
/* dorner@t500mo.telematik.informatik.uni-karlsruhe.de */
  if (strlen(genid) >= IDLEN) strcpy(&genid[IDLEN-2],">");
  return(genid);
}

void splashline(itmptr,vdptr,rowptr)
  ITM_PTR itmptr; int vdptr, rowptr;
{
  char *fdate;
  int li, ri, i;
  int status;

  _c$cks(smg$erase_line(&vdptr,&rowptr,c$ac(3)));

  sprintf(err_oline,"%-6d %-*.*s ",itmptr->itm_num,SUBJLEN,SUBJLEN,itmptr->itm_title);
  smg_put_chars(vdptr,err_oline,rowptr,3,0,0);

  if (minfromlen > 0 && !v59_file && devcol >= 80) {
    li = (67 - min(minfromlen,devcol - 30)) + (devcol - 80);
    ri = SUBJLEN + 9;
    i = min(li,ri);
    if (*(itmptr->itm_from)) {
      sprintf(err_oline," %-*.*s",FROMLEN,FROMLEN,itmptr->itm_from);
      smg_put_chars(vdptr,err_oline,rowptr,i,0,0);
      }
    li = devcol - 6;
    ri = i + minfromlen + 6;
    }
  else {
    li = devcol - 6;
    ri = SUBJLEN + 15;
    }
  i = max(li,ri);
  fdate = gendate(itmptr->itm_recvdate);
  if (*fdate || (itmptr->itm_flags & NEWS_M_LINESVALID)) {
    sprintf(err_oline," %-6.6s",fdate);
    smg_put_chars(vdptr,err_oline,rowptr,i,0,0);
    i -= 6;
    if (itmptr->itm_flags & NEWS_M_LINESVALID) {
      sprintf(err_oline,"%6d",itmptr->itm_lines);
      smg_put_chars(vdptr,err_oline,rowptr,i,0,0);
      }
    }
  if (itmptr->itm_flags & NEWS_M_UNREAD)
    _c$cks(smg$change_rendition(&vdptr,&rowptr,c$ac(3),c$ac(1),c$ac(6),c$ac(SMG$M_BOLD),0));
}

/*
 *  do_new_mem_itm
 *
 *  Add new item to mem array
 */

void do_new_mem_itm(g,gap)
  int g;
  GRP_PTR gap;
{
  int status;
  ITM_PTR iap;
  int i;

  i = ++gap->grp_count;
  ++gap->grp_unread;
  if (newsitm.itm_num < gap->grp_funread) gap->grp_funread = newsitm.itm_num;
  if ((gap->grp_display_indx) && (smg_active)) {
    sprintf(err_oline,"%5d",gap->grp_count);
    smg_put_chars(grp_vd,err_oline,gap->grp_display_indx,14+SUBJLEN,0,0);
    screen_update_gread(g);
    }

  if (gap->grp_ia) {
    if (gap->grp_iasize <= gap->grp_count)
      gap->grp_ia = (ITM_PTR) news_realloc(gap->grp_ia,(gap->grp_iasize +=4)*(sizeof newsitm));
    iap = gap->grp_ia;
    newsitm.itm_flags |= NEWS_M_UNREAD;
    gap->grp_ia[gap->grp_count] = newsitm;
    if (gap->grp_iavdsize && smg_active) {
      if (gap->grp_count > gap->grp_iavdsize)
        _c$cks(smg$change_virtual_display(&gap->grp_iavd,c$rfi(gap->grp_iavdsize += 4),c$ac(devcol),0,0,0));
      splashline(&(iap[i]),gap->grp_iavd,(int)(gap->grp_count));
      if (!gap->grp_c_itm) {
        gap->grp_c_itm = 1;
        smg_put_chars(gap->grp_iavd,"->",1,1,0,SMG$M_REVERSE+SMG$M_BOLD);
        position_display(&gap->grp_iavd,&gap->grp_iapaste,gap->grp_c_itm,0,gap->grp_count);
        if (g == curr_g) curr_i = 1;
        }
      }
    }
  else if ((smg_active) && (gap->grp_iavdsize)) {
    map_items(g);
    iap = gap->grp_ia;

    if (gap->grp_count > gap->grp_iavdsize)
      _c$cks(smg$change_virtual_display(&gap->grp_iavd,c$rfi(gap->grp_iavdsize += 4),
                                        c$ac(devcol),0,0,0));
    for (i = 1; i <= gap->grp_count; ++i) {
      splashline(&(iap[i]),gap->grp_iavd,i);
      if (iap[i].itm_flags & NEWS_M_UNREAD) {
        if (!gap->grp_c_itm) {
          gap->grp_c_itm = i;
          smg_put_chars(gap->grp_iavd,"->",i,1,0,SMG$M_REVERSE+SMG$M_BOLD);
          if (g == curr_g) curr_i = i;
          }
        }
      }
    if (!gap->grp_c_itm) {
      for (i = 1; i < gap->grp_count; ++i)
      if (iap[i].itm_flags & NEWS_M_UNREAD) break;
      gap->grp_c_itm = i;
      if (gap->grp_iavdsize)
        smg_put_chars(gap->grp_iavd,"->",i,1,0,SMG$M_REVERSE+SMG$M_BOLD);
      position_display(&gap->grp_iavd,&gap->grp_iapaste,gap->grp_c_itm,0,gap->grp_count);
      if (g == curr_g) curr_i = i;
      }
    }
}

/*
 *  do_open_item
 *
 *  Given an item record, open the associated text of the message.
 *  Use SYSPRV if WRITE access required, and change the mask to (g:re,w:re).
 */

extern char get_server_title[];
extern char get_server_from[];
extern char get_server_id[];
extern int  get_server_size;

FILE *do_open_item(g,i,mode,server_name)
  int g,i;
  const char *mode;
  char *server_name;
{
  FILE *tmp = 0;
  char *nntp_response;
  int nntp_respval;

  if (ga[g]->grp_ia[i].itm_flags & NEWS_M_MAILITEM) {
    if (*mode == 'r') return(read_mail(g,ga[g]->grp_ia[i].itm_cid,server_name));
    else return(0);
    }

  if (nntp_client) {
    nntp_respval = 211;
    if (strcmp(ga[g]->grp_name,nntp_currgroup)) {
      sprintf(err_oline,"GROUP %s",ga[g]->grp_name);
      nntp_respval = nntp_one_call(nntp_node,nntp_proto,err_oline,&nntp_response);
      if (nntp_respval == 211) strcpy(nntp_currgroup,ga[g]->grp_name);
      }
    if (nntp_respval == 211)
      tmp = getno_server(ga[g]->grp_ia[i].itm_num,ga[g]->grp_num,
                         nntp_node,server_name,0,nntp_proto);
    }
  else {
    sprintf(itm_fname,Itm_template,util_dir(ga[g]->grp_name),ga[g]->grp_ia[i].itm_num);
    if ((tmp = do_openi(mode,server_name,
			(ga[g]->grp_flags & NEWS_M_RESTRICT_SET),0)) != 0)
      return(tmp);
    if (strcmp(mode,"r") || (!(ga[g]->grp_flags & NEWS_M_NNTPSRV))) return(tmp);
    tmp = get_server_file(ga[g]->grp_ia[i].itm_id,ga[g]->grp_srvnode,
                   server_name,
		   ((ga[g]->grp_flags & NEWS_M_NNTPCACHE) ? itm_fname : 0),
		   ga[g]->grp_srvproto,g,i);
    }
  if (tmp) {
    if (!(ga[g]->grp_ia[i].itm_flags & NEWS_M_LINESVALID)) {
#if !NNTP_CLIENT_ONLY
      if (!nntp_client) {
        int status;

        itmrab.rab$l_kbf = (char *) &(ga[g]->grp_ia[i].itm_num);
        itmrab.rab$b_ksz = 8;
        itmrab.rab$b_krf = 0;
        itmrab.rab$l_rop = RAB$M_WAT;
        itmrab.rab$b_rac = RAB$C_KEY;
        if (sys_get_nornf(&itmrab)) {
          newsitm.itm_lines = get_server_size;
          newsitm.itm_flags |= NEWS_M_LINESVALID;
          util_subjcpy(newsitm.itm_title,get_server_title);
          util_fromcpy(newsitm.itm_from,get_server_from);
          if (ga[g]->grp_flags & NEWS_M_NNTPCACHE)
	    time((time_t *)&newsitm.itm_cachedate);
          sys_update(&itmrab);
          }
        util_subjcpy(ga[g]->grp_ia[i].itm_title,get_server_title);
        util_fromcpy(ga[g]->grp_ia[i].itm_from,get_server_from);
        }
#endif
      if (*get_server_id) util_idcpy(ga[g]->grp_ia[i].itm_id,get_server_id);
      ga[g]->grp_ia[i].itm_lines = get_server_size;
      ga[g]->grp_ia[i].itm_flags |= NEWS_M_LINESVALID;
      }
    util_fromcpy(ga[g]->grp_ia[i].itm_from,get_server_from);
    if (ga[g]->grp_flags & NEWS_M_NNTPCACHE)
      time((time_t *)&(ga[g]->grp_ia[i].itm_cachedate));
    else ga[g]->grp_ia[i].itm_cachedate = 0;
    if (smg_active && ga[g]->grp_iavdsize)
      splashline(&(ga[g]->grp_ia[i]),ga[g]->grp_iavd,i);
    }
  return(tmp);
}

/*
 * find_itm_by_id - locate item by id
 *                  return !0 if item found, with *ip = index into grp_ia
 *                  return  0 if no item found, with *ip unchanged
 */

unsigned int find_itm_by_id(g,id,ip)
  unsigned int g, *ip;
  const char *id;
{
  register unsigned int i;

  if (!map_ids(g)) return(0);
  for (i = 1; i <= ga[g]->grp_count; i++)
    if (!news_strncasecmp(ga[g]->grp_ia[i].itm_id,id,0)) return(*ip = i);
  return(0);

}

/*
 * find_itm_by_num - maps item number to ia offset
 *                   returns !0 if found, with offset in *ip
 *                   returns  0 if not exact match, with nearest offset
 *                   in *ip (iap[*ip] > itm_num preferred)
 */

unsigned int find_itm_by_num(g,itmnum,ip)
  unsigned int g, itmnum;
  int *ip;
{                         
  static unsigned int prev_g, prev_iidx;

  if (!ga[g]->grp_ia) map_items(g);
  if (!ga[g]->grp_ia) return(*ip = 0);

  /* we expect serial lookups often, so we handle them as a special case */
  if ( (g == prev_g) && (ga[g]->grp_ia[prev_iidx+1].itm_num == itmnum) )
     return(*ip = ++prev_iidx);

  if (ga[g]->grp_flags & NEWS_M_ORDERBYSUBJECT) {
    register int i;
    for (i = 1; i <= ga[g]->grp_count; i++)
      if (ga[g]->grp_ia[i].itm_num == itmnum)
        return(prev_g = g,prev_iidx = *ip = i);
    *ip = 1;
    return(0);
    }
  else {
    register unsigned int incr, iter, tmp, maxiter = 2;
    register int cmp;

    iter = ga[g]->grp_count;
    tmp = incr = (ga[g]->grp_count > 1) ? ga[g]->grp_count / 2 : 1;
    while ( (iter=(iter>>1)) ) maxiter++;  /* set maxiter and clear iter */
    while (1) {
      cmp = itmnum - ga[g]->grp_ia[tmp].itm_num;
      if (!cmp) return(prev_g = g,prev_iidx = *ip = tmp);
      if ( (tmp == 1) && (cmp < 0) ) {
        *ip = 1;
        return(0);
        }
      if ( (tmp == ga[g]->grp_count) && (cmp > 0) ) {
        *ip = ga[g]->grp_count;
        return(0);
        }
      if (++iter == maxiter) {
        *ip = (cmp > 0) ? ++tmp : tmp;
        return(0);
        }
      tmp += incr * ((cmp < 0) ? -1 : 1);
      if (tmp < 1) tmp = 1;
      if (tmp > ga[g]->grp_count) tmp = ga[g]->grp_count;
      incr = (++incr)/2;
      }
    }
  *ip = 1; return(0);  /* can not happen */
}

static char *s_newsgroup = 0;
static int s_inum = 0;

static int snewsgroup(header_line)
  char *header_line;
{
  char *cp;

  if (!s_inum && !strncmp(header_line,"Xref:",5)) {
    header_line += 5;
    while (isspace(*header_line)) ++header_line;
    if ((cp = chop_str_plus(header_line,':')) != 0) {
      if (s_newsgroup) news_free(s_newsgroup);
      s_newsgroup = (char *) news_malloc(strlen(header_line) + 1);
      strcpy(s_newsgroup,header_line);
      lower_case(s_newsgroup);
      sscanf(cp,"%d",&s_inum);
      }
    return 0;
    }
  if (strncmp(header_line,"Newsgroups:",11)) return 0;
  chop_str(header_line,'\n');
  cp = &header_line[11];
  while (isspace(*cp)) ++cp;
  s_newsgroup = (char *) news_malloc(strlen(cp) + 1);
  strcpy(s_newsgroup,cp);
  lower_case(s_newsgroup);
  return 1;
}

static int sinum(resp_line)
  char *resp_line;
{
  if (sscanf(resp_line,"221 %d",&s_inum) != 1) s_inum = 0;
  return 1;
}

static int fib(header_line)
  char *header_line;
{
  char *cp;

  chop_str(header_line,'\n');
  if (!news_strncasecmp(header_line,"From:",5)) {
    cp = &header_line[6];
    while (isspace(*cp)) ++cp;
    util_fromcpy(newsitm.itm_from,cp);
    }
  else if (!news_strncasecmp(header_line,"Subject:",8)) {
    cp = &header_line[8];
    while (isspace(*cp)) ++cp;
    util_subjcpy(newsitm.itm_title,cp);
    }
  if (!news_strncasecmp(header_line,"Message-ID:",11)) {
    cp = &header_line[11];
    while (isspace(*cp)) ++cp;
    util_idcpy(newsitm.itm_id,cp);
    }
  else if (!news_strncasecmp(header_line,"Date:",5)) {
    int tmp;

    cp = &header_line[6];
    while (isspace(*cp)) ++cp;
    if ((tmp = parse_usenet_date(cp)) != 0) newsitm.itm_postdate = tmp;
    }
  else if (!news_strncasecmp(header_line,"Lines:",5)) {
    if (sscanf(header_line,"Lines: %d",&newsitm.itm_lines) == 1)
      newsitm.itm_flags |= NEWS_M_LINESVALID;
    }
  return 1;
}

int get_itm_by_ident(id,rg,ri,def_g)
  char *id;
  int *rg, *ri,def_g;
{
  char *cp1, *cp2, *cp3, *cp4, *nntp_response;
  int count, first, last, g, i;
  int status;

#if !NNTP_CLIENT_ONLY
  if (!nntp_client) {
    unsigned int *gr;
    char pref[512];

    util_idcpy(pref,id);
    gr = (unsigned int *) &(pref[IDLEN]);
    *gr = ga[curr_g]->grp_num;
    itmrab.rab$l_kbf = pref;
    itmrab.rab$b_ksz = IDLEN + 4;
    itmrab.rab$b_krf = 1;
    itmrab.rab$l_rop = RAB$M_RRL | RAB$M_NLK;
    itmrab.rab$b_rac = RAB$C_KEY;
    g = curr_g;
    if (!sys_get_nornf(&itmrab)) {
      *gr = 0;
      itmrab.rab$l_rop = RAB$M_KGE | RAB$M_RRL | RAB$M_NLK;
      if ((!sys_get_nornf(&itmrab)) || (strcmp(pref,newsitm.itm_id))) return(0);
      if (!(g = ga_locate(newsitm.itm_grp))) return(0);
      }
    s_inum = newsitm.itm_num;
    *rg = g;
    if (!ga[g]->grp_ia) map_items(g);
    if (!ga[g]->grp_ia) return(0);
    if (!find_itm_by_num(g,s_inum,&i)) return(0);
    *ri = i;
    return(1);
    }
#endif
  if (s_newsgroup) news_free(s_newsgroup);
  s_newsgroup = 0;
  s_inum = 0;
  sprintf(err_oline,"HEAD %s",id);
  if (!nntp_get_info(nntp_node,nntp_proto,err_oline,221,sinum,snewsgroup)) {
    if (def_g) {
      s_newsgroup = news_malloc(strlen(ga[def_g]->grp_name) + 1);
      strcpy(s_newsgroup,ga[def_g]->grp_name);
      }
    else return(0);
    }
  cp1 = s_newsgroup;
  do {
    if (cp1 == ga[curr_g]->grp_name) {
      cp2 = s_newsgroup;
      g = curr_g;
      }
    else {
      cp2 = chop_str_plus(cp1,',');
      if (!(g = ga_exact_name(cp1))) continue;
      }
    if (find_itm_by_id(g, id, (unsigned int *)ri)) return(1);
    sprintf(err_oline,"GROUP %s",cp1);
    if (nntp_one_call(nntp_node,nntp_proto,err_oline,&nntp_response) != 211) continue;
    strcpy(nntp_currgroup,cp1);
    if (sscanf(nntp_response,"211 %d %d %d",&count,&first,&last) != 3) continue;
    if (s_inum && ((s_inum < first) || (s_inum > last))) continue;
    if (!s_inum) {
      IDMAP_PTR tmp = ga[g]->grp_idhead;

      if (tmp) do {
        if (!news_strncasecmp(tmp->id,id,0)) {
          s_inum = tmp->itm_num;
          tmp->itm_num = (ga[g]->grp_idtail)->itm_num;
          util_idcpy(tmp->id,(ga[g]->grp_idtail)->id);
          tmp = ga[g]->grp_idtail;
          if (!(ga[g]->grp_idtail = tmp->b_link)) ga[g]->grp_idhead = 0;
          news_free(tmp);
          (ga[g]->grp_idtail)->f_link = 0;
          break;
          }
        } while ( (tmp=tmp->f_link) );
      if (!s_inum) continue;
      }
    sprintf(err_oline,"STAT %d",s_inum);
    if (nntp_one_call(nntp_node,nntp_proto,err_oline,&nntp_response) != 223) continue;
    if (!(cp3 = strchr(nntp_response,'<'))) continue;
    if ((cp4 = strrchr(cp3,'>')) != 0) *++cp4 = '\0';
    if (news_strncasecmp(cp3,id,0)) continue;
    *rg = g;
    if (!ga[g]->grp_ia) map_items(g);
    if (!ga[g]->grp_ia) continue;
    if (!find_itm_by_num(g,s_inum,&i)) {
      int sav_inum = 0, loc_i;
      GRP_PTR gap = ga[g];

      if ((curr_g == g) && (news_context > 1)) {
        if (curr_i) sav_inum = gap->grp_ia[curr_i].itm_num; else sav_inum = -1;
        set_level(1);
        }
      if (gap->grp_iavdsize) _c$cks(smg$delete_virtual_display(&gap->grp_iavd));
      gap->grp_iavdsize = 0;
      sprintf(err_oline,"HEAD %d",s_inum);
      newsitm.itm_num = s_inum;
      newsitm.itm_grp = g;
      util_idcpy(newsitm.itm_id,id);
      time((time_t *)&newsitm.itm_recvdate);
      newsitm.itm_lines = 0;
      newsitm.itm_title[0] = '\0';
      newsitm.itm_cachedate = 0;
      newsitm.itm_flags = 0;
      newsitm.itm_cid = 0;
      newsitm.itm_from[0] = '\0';
      if (!nntp_get_info(nntp_node,nntp_proto,err_oline,221,0,fib)) continue;
      newsitm.itm_postdate = newsitm.itm_recvdate;
      if (!gap->grp_ia) {
        gap->grp_iasize = gap->grp_count + 2;
        gap->grp_ia = (ITM_PTR) news_malloc((gap->grp_iasize + 1) * (sizeof newsitm));
        gap->grp_count = 0;
        }
      else if (gap->grp_iasize <= gap->grp_count)
        gap->grp_ia = (ITM_PTR) news_realloc(gap->grp_ia,(gap->grp_iasize +=4)*(sizeof newsitm));
      ++gap->grp_count;
      for (i = gap->grp_count; i > 1; --i) {
        if (gap->grp_ia[i-1].itm_num > s_inum) gap->grp_ia[i] = gap->grp_ia[i-1];
        else {
          gap->grp_ia[i] = newsitm;
          break;
          }
        }
      if (i == 1) gap->grp_ia[1] = newsitm;
      if (sav_inum) {
	set_level(2);
        if ( (sav_inum > 0) && find_itm_by_num(g,sav_inum,&loc_i) )
          cur_set_itm(g,loc_i);
        }
      }
    *ri = i;
    return(1);
    } while ((cp1 = cp2) != 0);
  return(0);
}

/*
 * get_itm_by_num
 *
 * Retrieve an item by its group and item number.  If successful,
 * returns 1, and *itmidx is the index into ga[grp]->grp_ia of
 * the record for the item sought.  If the item cannot be found,
 * get_itm_by_num returns 0, and *itmidx is unchanged.
 */

int get_itm_by_num(grp,itmnum,itmidx)
  int grp,itmnum,*itmidx;
{
  int status;
  char *nntp_response;
  int count, first, last, sav_inum = 0, i, loc_i;

  if (!ga[grp]->grp_ia) map_items(grp);
  if (find_itm_by_num(grp,itmnum,itmidx)) return 1;

#if !NNTP_CLIENT_ONLY
  if (!nntp_client) {
    int key[2];
    key[0] = itmnum;
    key[1] = ga[grp]->grp_num;
    itmrab.rab$l_kbf = (char *) key;
    itmrab.rab$b_ksz = 8;
    itmrab.rab$b_krf = 0;
    itmrab.rab$l_rop = RAB$M_KGE | RAB$M_RRL | RAB$M_NLK;
    itmrab.rab$b_rac = RAB$C_KEY;

    if (!sys_get_nornf(&itmrab)) return 0;
    if (newsitm.itm_grp != ga[grp]->grp_num ||
        newsitm.itm_num != itmnum) return 0;
    }
  else {
#endif
    sprintf(err_oline,"GROUP %s",ga[grp]->grp_name);
    if (nntp_one_call(nntp_node,nntp_proto,err_oline,&nntp_response) != 211)
      return 0;
    strcpy(nntp_currgroup,ga[grp]->grp_name);
    if (sscanf(nntp_response,"211 %d %d %d",&count,&first,&last) != 3) return 0;
    if (itmnum < first || itmnum > last) return 0;
    sprintf(err_oline,"STAT %d",itmnum);
    if (nntp_one_call(nntp_node,nntp_proto,err_oline,&nntp_response) != 223)
      return 0;
    sprintf(err_oline,"HEAD %d",itmnum);
    newsitm.itm_num = itmnum;
    newsitm.itm_grp = grp;
    time((time_t *)&newsitm.itm_recvdate);
    newsitm.itm_lines = 0;
    newsitm.itm_title[0] = '\0';
    newsitm.itm_cachedate = 0;
    newsitm.itm_flags = 0;
    newsitm.itm_cid = 0;
    newsitm.itm_from[0] = '\0';
    if (!nntp_get_info(nntp_node,nntp_proto,err_oline,221,0,fib)) return 0;
    newsitm.itm_postdate = newsitm.itm_recvdate;
#if !NNTP_CLIENT_ONLY
    }      
#endif

  if ((curr_g == grp) && (news_context > 1)) {
    if (curr_i) sav_inum = ga[grp]->grp_ia[curr_i].itm_num; else sav_inum = -1;
    set_level(1);
    }
  if (ga[grp]->grp_iavdsize) {
    _c$cks(smg$delete_virtual_display(&ga[grp]->grp_iavd));
    ga[grp]->grp_iavdsize = 0;
    }
  if (!ga[grp]->grp_ia) {
    ga[grp]->grp_iasize = ga[grp]->grp_count + 2;
    ga[grp]->grp_ia = (ITM_PTR) news_malloc((ga[grp]->grp_iasize + 1) * (sizeof newsitm));
    ga[grp]->grp_count = 0;
    }
  else if (ga[grp]->grp_iasize <= ga[grp]->grp_count)
    ga[grp]->grp_ia = (ITM_PTR) news_realloc(ga[grp]->grp_ia,(ga[grp]->grp_iasize +=4)*(sizeof newsitm));
  ++ga[grp]->grp_count;
  for (i = ga[grp]->grp_count; i  > 1; --i) {
    if (ga[grp]->grp_ia[i-1].itm_num > itmnum) ga[grp]->grp_ia[i] = ga[grp]->grp_ia[i-1];
    else {
      ga[grp]->grp_ia[i] = newsitm;
      break;
      }
    }
  if (i == 1) ga[grp]->grp_ia[1] = newsitm;
  if (sav_inum) {
    set_level(2);
    if ( (sav_inum > 0) && find_itm_by_num(grp,sav_inum,&loc_i) )
      cur_set_itm(grp,loc_i);
    }
  *itmidx = i;
  return 1 ;
}

/*
 *  do_open_header
 *
 *  Given an item record, open the associated header of the message.
 */

FILE *do_open_header(g,i,mode,server_name)
  int g,i;
  const char *mode;
  char *server_name;
{
  FILE *tmp;
  int status;

  if (!nntp_client) {
    sprintf(itm_fname,Itm_template,util_dir(ga[g]->grp_name),ga[g]->grp_ia[i].itm_num);
    if ((tmp = do_openi(mode,server_name,
			(ga[g]->grp_flags & NEWS_M_RESTRICT_SET),0)) != 0)
      return(tmp);
    if (strcmp(mode,"r") || (!(ga[g]->grp_flags & NEWS_M_NNTPSRV)))
      return(tmp);
    }
  tmp = get_server_header(ga[g]->grp_ia[i].itm_id,ga[g]->grp_srvnode,server_name,
			  ga[g]->grp_srvproto,ga[g]->grp_name,ga[g]->grp_ia[i].itm_num);
  if (tmp) {
    if (!(ga[g]->grp_ia[i].itm_flags & NEWS_M_LINESVALID)) {
      itmrab.rab$l_kbf = (char *) &(ga[g]->grp_ia[i].itm_num);
      itmrab.rab$b_ksz = 8;
      itmrab.rab$b_krf = 0;
      itmrab.rab$l_rop = RAB$M_WAT;
      itmrab.rab$b_rac = RAB$C_KEY;
      if (sys_get_nornf(&itmrab)) {
        newsitm.itm_lines = get_server_size;
        newsitm.itm_flags |= NEWS_M_LINESVALID;
        util_subjcpy(newsitm.itm_title,get_server_title);
        util_fromcpy(newsitm.itm_from,get_server_from);
        sys_update(&itmrab);
        }
      ga[g]->grp_ia[i].itm_lines = get_server_size;
      ga[g]->grp_ia[i].itm_flags |= NEWS_M_LINESVALID;
      util_subjcpy(ga[g]->grp_ia[i].itm_title,get_server_title);
      util_fromcpy(ga[g]->grp_ia[i].itm_from,get_server_from);
      }
    if (smg_active && ga[g]->grp_iavdsize)
      splashline(&(ga[g]->grp_ia[i]),ga[g]->grp_iavd,i);
    }
  return(tmp);
}

/*
 *  check_command
 *
 *  command parser without dispatch
 */

static char s_param[256];

static int itf()
{
  if (news_context > 1) {
    int i;

    if (!curr_g) return(0);
    if (!(i = item_find(s_param))) return(0);
    cur_set_itm(curr_g,i);
    return(0);
    }
  else if (smg_active && curr_g && ga[curr_g]->grp_display_indx) {
    int num = 1;

    sscanf(s_param,"%d",&num);
    if (num < ga[curr_g]->grp_display_indx) cur_up_grp(ga[curr_g]->grp_display_indx - num,1);
    else if (num > ga[curr_g]->grp_display_indx) cur_down_grp(num - ga[curr_g]->grp_display_indx,1);
    return(0);
    }
  return(do_error(),0);
}

static int dcitm()
{
  if (curr_i <= 0) return(do_error(),0);
  return(do_readfind(s_param,0,0),0);
}

int chk_val = -1;

static int check_command(s,s_l)
  const char *s;
  int s_l;  /* actual string length (must not rely on '\0'), or -1 it unknown */
{
  if (s_l < 0) s_l = strlen(s);

  if (s_l > 0  &&  ((*s == '!') || (*s == ';'))) return(chk_val = 0);
  if (s_l == 0 || !*s) return(chk_val = 1);
  if (isdigit(*s)) return(chk_val = 2);
  if (s_l == 1 && *s == '.') return(chk_val = 3);

  { struct dsc$descriptor_const_s *d = (struct dsc$descriptor_const_s *) c$alloc_tmp(8);
    d->dsc$b_dtype = DSC$K_DTYPE_T;  d->dsc$b_class = DSC$K_CLASS_S;
    d->dsc$a_pointer = s; d->dsc$w_length = s_l;
    if (cli$dcl_parse(d,CLICMDTBL,get_input,get_input,c$dsc("NEWS> ")) & 1)
      return(chk_val = 4);
    return(chk_val = 0);
  }
}
/*
 *  do_parse
 *
 *  command parser
 */

int
do_parse(s)
  const char *s;
{
  int parse_val, s_l;

  if (!s) s_l = 0;
  else {
    while (*s && isspace(*s)) s++;
    s_l = strlen(s);
    while (s_l && isspace(s[s_l-1])) --s_l;
    }

  if ((parse_val = chk_val) == -1) parse_val = check_command(s,s_l);
  chk_val = -1;
  if (!parse_val) return(0);

  if (parse_val == 1) {
    /* This procedure is called from two locations: at the main parse loop and within
     * the text display loop. If in the text display loop, then parse is not called
     * with an empty string - hence the parse_val does not need to consider
     * stack unwind operations
     */
    if ((news_context == 1) && (curr_g)) return(do_select("%",curr_g,3),0);

    else if (old_context == 3) {
      if (*profile_endofitm_cmd) return(do_parse(profile_endofitm_cmd));
      else return(do_readnext(0,0,0),0);
/*
           return(do_readfollow(0,0,1),0);
 */
      }
    else {
      if ((curr_g) && (curr_i)) {
        markasread(curr_g,curr_i,1);
        rpush(curr_g,curr_i);
        do_display(0,0,0);
        }
      return(0);
      }
    }
  if (parse_val == 2) {
    /* The input is a digit - this is a stack unwind operation */
    news_assert(s_l < sizeof(s_param));
    memcpy(s_param,s,s_l); s_param[s_l] = '\0';
    if (news_context > 2) return(unwind_display(OUTER_LOOP,dcitm));
    return(unwind_display(OUTER_LOOP,itf));
    }
  if (parse_val == 3) {
    /* The input is a '.' - this is a stack unwind operation */
    news_assert(s_l < sizeof(s_param));
    memcpy(s_param,s,s_l); s_param[s_l] = '\0';
    return(unwind_display(OUTER_LOOP,dcitm));
    }
  if (parse_val == 4) return(cli$dispatch());
  return(0);
}

void
write_err_line(s)
  const char *s;
{
  int status;

  err_line(s);
  if (smg_active) {
    _c$cks(smg$end_pasteboard_update(&pid));
    _c$cks(smg$begin_pasteboard_update(&pid));
    }
}

/*
 *  ga_locate
 *
 *  locate newsgroup index from newsgroup num
 */

int
ga_locate(g)
  unsigned int g;
{
  int i;

  for (i = 1; i <= ga_size; ++i)
    if (g == ga[i]->grp_num) return(i);
  return(0);
}

/*
 *  ga_search_name
 *
 *  locate a newsgroup by name
 */

int ga_search_name(s)
  const char *s;
{
  int j = 0, i, l = strlen(s);
  char c = *s;

  for (i = 1 ; i <= ga_size ; ++i) {
    if (*ga[i]->grp_name != c) continue;
    if (!strcmp(s,ga[i]->grp_name)) return(i);
    if (!strncmp(s,ga[i]->grp_name,l)) j = i;
    }
  return(j);
}

/*
 *  ga_exact_name
 *
 *  locate a newsgroup by name - exact match
 */


static int ga_exact_compare(v_key, v_test)
  const void *v_key, *v_test;
{
  const char *key = v_key;
  const GRP_PTR *test = v_test;

  return strcmp(key, (*test)->grp_name);
}
  
int ga_exact_name(s)
  const char *s;
{
  static int prev_gi = 0;
  GRP_PTR *ga_i;
  int i, slen;

  /* chance of consecutive lookup attempt is high enough to make a special
     check worthwhile
   */
  if (++prev_gi <= ga_size && !strcmp(s,ga[prev_gi]->grp_name))
    return prev_gi;

  if ( ! reorder_groups ) {  /* perform binary search */
    ga_i = (GRP_PTR *) bsearch(s, ga+1, ga_size, sizeof *ga, ga_exact_compare);
    return prev_gi = (ga_i ? (ga_i - ga) : 0);
  }
/* else use old code. */
  slen = strlen(s);
  if (slen >= SUBJLEN)
      slen = SUBJLEN - 1;
  slen = (slen > 0) ? slen - 1 : 0;

  /* This little trick lets us do single byte compares on each newsgroup.
     Assuming that the group for which we are searching has a name of less than
     SUBJLEN characters (#define'd maximum), then slen should be a valid index
     into the character array pointing at the last character.  This essentially
     checks to see if the two group names have the same length. */

  for (i = 1; i <= ga_size; ++i)
    if (s[slen] == ga[i]->grp_name[slen])
        if (!strcmp(s, ga[i]->grp_name))
            return (i);
  return(0);
}

/*
 *  create_pb
 *
 *  create the pasteboard
 */

#ifdef __DECC
#pragma member_alignment save
#pragma nomember_alignment   /* no member alignment - this is a VMS structure */
#endif
struct {
    unsigned long int SMG$L_DEVCHAR;
    unsigned long int SMG$L_DEVDEPEND;
    unsigned long int SMG$L_DEVDEPEND2;
    unsigned char SMG$B_DEVCLASS;
    unsigned char SMG$B_SMG_DEVTYPE;
    unsigned char SMG$B_PHY_DEVTYPE;
    unsigned char SMG$B_ROWS;
    unsigned short int SMG$W_WIDTH;
    unsigned char SMG$B_COLOR;
    unsigned char SMG$B_PARITY;
    unsigned short int SMG$W_SPEED;
    unsigned short int SMG$W_FILL;
    unsigned short int SMG$W_CURSOR_ROW;
    unsigned short int SMG$W_CURSOR_COL;
    unsigned long int SMG$L_CURSOR_DID;
    } pb_info;

/* member alignment is required to assure atomic volatile access */
#ifdef __DECC
#pragma member_alignment
#endif
static volatile struct bmsg {
  char *bm;
  struct bmsg *bnext;
  } *bhead = 0, *btail = 0;

#ifdef __DECC
#pragma member_alignment restore
#endif

static int smg_term = -1;
static volatile int bcount = 0, bbytecount = 0;

int line_width = 80;

int
do_clear_messages()
{
  err_line("Message Buffer cleared");
  while (bhead) {
    btail = bhead ;
    bhead = bhead->bnext;
    news_free((void *) btail->bm);
    news_free((void *) btail);
    }
  btail = 0;
  bcount = bbytecount = 0;
  return(0);
}

void push_brdmsg(str)
  const char *str;
{
  struct bmsg *tmp_bmsg;
  char *tmp_str;
  int str_l = strlen(str);

  if ((tmp_bmsg = (struct bmsg *) news_malloc(sizeof *btail)) &&
      (tmp_str  = (char *) news_malloc(str_l+1))) {
    tmp_bmsg->bnext = 0;  strcpy(tmp_bmsg->bm = tmp_str, str);
    if (!btail) bhead = btail = tmp_bmsg;
    else { btail->bnext = tmp_bmsg; btail = tmp_bmsg; }
    bbytecount += str_l;  ++bcount;
  }
}

/*
 *  push_brdmsg_raw
 *
 *  This routine is functionally equivalent to push_brdmsg()
 *  except that it is ment to be called from the condition
 *  handler and must not produce/signal another error - so it does
 *  its job if possible or gives up silently otherwise.
 *
 *  The use of news_malloc_raw() instead of news_alloc() is intentional !
 *  news_malloc_raw() must not signal exceptions !
 *
 *  See the comment in NEWSRTL.C regarding the use of
 *  raw memory allocation routines.
 */
void push_brdmsg_raw(str)
  char *str;
{
  struct bmsg *tmp_bmsg;
  char *tmp_str;
  int str_l = strlen(str);

  if ((tmp_bmsg = (struct bmsg *) news_malloc_raw(sizeof *btail)) &&
      (tmp_str  = (char *) news_malloc_raw(str_l+1))) {
    tmp_bmsg->bnext = 0;  strcpy(tmp_bmsg->bm = tmp_str, str);
    if (!btail) bhead = btail = tmp_bmsg;
    else { btail->bnext = tmp_bmsg; btail = tmp_bmsg; }
    bbytecount += str_l;  ++bcount;
  }
}

/*
 *  err_line
 *
 *  error line management
 */

void
err_line(s)
  const char *s;
{
#if STRIP_ERR_LINES
  if (!strncmp(s,"Error",5) || !strncmp(s,"\tError",6)) {
    const char *cp = &s[6];

    while (*cp && !isalnum(*cp)) cp++;
    if (*cp) s = cp;
    }
#endif
  push_brdmsg(s);
  if (!smg_active) printf("\t%s\n",s);
  else smg_put_chars(trailer_vd,s,3,1,1,SMG$M_REVERSE);
}

static int
dsm_1()
{
  struct bmsg *b;

  if (!bhead) {
    strcpy(err_oline,"Show Messages - message buffer is empty");
    if (!smg_active) printf("\t%s\n",err_oline);
    else smg_put_chars(trailer_vd,err_oline,3,1,1,SMG$M_REVERSE);
    return(0);
    }
  textptr.str_searching = 1;
  strcpy(textptr.str_target,"#### END of MESSAGE BUFFER ####");
  textptr.str_length = strlen(textptr.str_target);
  textptr.str_case_sensitive = 1;
  start_header(T_DISPLAY_LOOP,SHOW_MESSAGES);
  textptr.str_found = 0;
  sprintf(err_oline,"Message Buffer           Total size: %d messages",bcount);
  put_line(err_oline,0,T_DISPLAY_LOOP);
  end_header(T_DISPLAY_LOOP);
  textptr.text_cline = 0;
  textptr.text_lines = bcount;
  textptr.dispf_stat.st_size = bbytecount;
  for (b = (struct bmsg *) bhead; b; b = b->bnext) {
    textptr.gets_size += strlen(b->bm);
    put_line(b->bm,0,T_DISPLAY_LOOP);
    }
  put_line("#### END of MESSAGE BUFFER ####",0,T_DISPLAY_LOOP);
  put_line("",1,T_DISPLAY_LOOP);
  textptr.str_searching = 0;
  return(0);
}

int
do_show_messages()
{
  return(unwind_display(I_DISPLAY_LOOP,dsm_1));
}

void messages_write(f)
  FILE *f;
{
  struct bmsg *b;
  for (b = (struct bmsg *) bhead; b; b = b->bnext) {
    if (b == bhead && strncmp(b->bm,"ANU News -",10) == 0) ;  /* ignore */
    else {
      putl("# ",f);  _ck_put(f);
      putl(b->bm,f); _ck_put(f);
      fputc('\n',f); _ck_put(f);
      }
    }
}

void display_brdcst(line)	int line;
{
  char bm[2050];
  unsigned short bm_len;
  $DESCRIPTOR(bm_dsc,bm);
  int sts, status;
  int bell_cnt, dst, j, at_the_beginning;

  if (((sts = smg$get_broadcast_message(&pid,&bm_dsc,&bm_len,0)) & 1)
      && (sts != SMG$_NO_MORMSG) && (sts != SMG$_NONBRDMSG)) {
    bm[bm_len] = '\0';

    /* remove and count <BEL> chars and remove <CR> before line terminators */
    bell_cnt = 0; dst = 0;
    for (j=0; j<bm_len; j++) {
      if (bm[j]=='\007') bell_cnt++;
      else if ((bm[j]=='\r') &&
               (bm[j+1]=='\n' || bm[j+1]=='\v' || bm[j+1]=='\f')) /* ignore */;
      else bm[dst++] = bm[j];
      }
    bm[dst] = '\0';  bm_len = dst;

    if (bell_cnt > 3) bell_cnt = 3;
    if (bell_cnt > 0) _c$cks(smg$ring_bell(&trailer_vd,&bell_cnt));

    _c$cks(smg$erase_line(&trailer_vd,&line,c$ac(1)));
    smg_put_chars(trailer_vd,bm,line,1,1,SMG$M_BOLD);
    _c$cks(smg$set_cursor_abs(&trailer_vd,c$ac(1),c$ac(brdcst_col)));

    /* save multi-line messages as several messages */
    for (j=0; j<bm_len; j++) {
      if ((bm[j]=='\n' || bm[j]=='\v' || bm[j]=='\f')) bm[j]='\0';
      }
    at_the_beginning = 1;
    for (j=0; j<bm_len; j++) {
      if (bm[j]=='\0') at_the_beginning = 1;
      else if (at_the_beginning) { push_brdmsg(&bm[j]); at_the_beginning = 0; }
      }
    }
}

void b_trap()
{
  if (brdcst_line) display_brdcst(brdcst_line);
}

int create_pb()
{
  int status;

  if (pid_created) return(1);
  if (smg_term >= 0) return(smg_term);
  _c$cks(smg$create_pasteboard(&pid,0,&devrow,&devcol,c$ac(0)));
  pid_created = 1;
  _c$cks(smg$get_pasteboard_attributes(&pid,&pb_info,c$ac(sizeof pb_info)));
  if (pb_info.SMG$B_DEVCLASS != DC$_TERM) {
    printf("\n\tNEWS: Not a terminal - /NOSCREEN mode used\n");
    pid_created = 0;
    }
  else if (pb_info.SMG$B_SMG_DEVTYPE != SMG$K_VTTERMTABLE) {
    printf("\n\tNEWS: Not an SMG supported terminal - /NOSCREEN mode used\n");
    pid_created = 0;
    }
  line_width = pb_info.SMG$W_WIDTH;
  if (!pid_created) _c$cks(smg$delete_pasteboard(&pid,c$ac(0)));
  return(smg_term = pid_created);
}

/*
 *  init_screen
 *
 *  set up the screen display
 */

int init_screen()
{
  int num_grps;

  if (smg_active) return(0);
  if (!create_pb()) {
    printf("\nError: SCREEN - Not an SMG terminal\n");
    return(0);
    }
  set_level(1);
  smg_active = 1;
  num_grps = ga[0]->grp_count;
  c$cks(smg$create_virtual_display(c$rfi(num_grps + 40),c$ac(devcol),&grp_vd,0,0,0));
  gv_size = num_grps + 40;
  c$cks(smg$create_virtual_display(c$ac(3),c$ac(devcol),&grp_header_vd,0,c$ac(SMG$M_REVERSE + SMG$M_BOLD),0));
  c$cks(smg$create_virtual_display(c$ac(3),c$ac(devcol),&itm_header_vd,0,c$ac(SMG$M_REVERSE + SMG$M_BOLD),0));
  c$cks(smg$create_virtual_display(c$ac(3),c$ac(devcol),&trailer_vd,0,0,0));
  c$cks(smg$create_virtual_display(c$rfi(DISP_BUF_SIZE + devrow),c$ac(devcol),&(itmptr.sdid),0,0,0));
  c$cks(smg$create_virtual_display(c$rfi(DISP_BUF_SIZE + devrow),c$ac(devcol),&(textptr.sdid),0,0,0));
  c$cks(smg$set_display_scroll_region(&(itmptr.sdid),c$ac(1),c$ac(DISP_BUF_SIZE)));
  c$cks(smg$set_display_scroll_region(&(textptr.sdid),c$ac(1),c$ac(DISP_BUF_SIZE)));
  c$cks(smg$create_virtual_display(c$rfi(devrow - 3),c$ac(devcol),&(itmptr.shdid),0,0,0));
  c$cks(smg$create_virtual_display(c$rfi(devrow - 3),c$ac(devcol),&(textptr.shdid),0,0,0));

  smg_put_chars(grp_header_vd,"NEWS       NEWSGROUP LISTING",1,4,0,0);
  smg_put_chars(grp_header_vd,"        Newsgroup                                           Count    Unread",
                              3,4,0,0);
  smg_put_chars(itm_header_vd,"NEWS ITEMS",1,4,0,0);
  smg_put_chars(itm_header_vd,"      Title",3,4,0,0);

  c$cks(smg$begin_pasteboard_update(&pid));
  c$cks(smg$erase_pasteboard(&pid));
  do_refresh();
  c$cks(smg$paste_virtual_display(&grp_vd,&pid,c$ac(4),c$ac(1)));
  c$cks(smg$paste_virtual_display(&grp_header_vd,&pid,c$ac(1),c$ac(1)));
  c$cks(smg$paste_virtual_display(&trailer_vd,&pid,c$rfi(devrow - 2),c$ac(1)));

  screen_map_dir();

  chg_mode();
  if (broadcast_trapping_requested) broad_trap(); else no_broad_trap();
  screen_displays_setup = 1;
  return(0);
}

int do_screen()
{
  return(unwind_display(OUTER_LOOP,init_screen));
}

/*
 *  item_find
 *
 *  locate a news item as specified by item number
 */

int item_find(s)
char *s;
{
  int locnum,
      i;

  if (!curr_g) {
    err_line("No Group Selected");
    return(0);
    }

  if (!ga[curr_g]->grp_count) return(0);

  if ((s) && (isdigit(*s))) {
    set_level(2);
    if (sscanf(s,"%d",&locnum) != 1) return(0);
    if (locnum <= ga[curr_g]->grp_ia[1].itm_num) {
      cur_up_itm(curr_g,ga[curr_g]->grp_count,0);
      return(curr_i);
      }
    find_itm_by_num(curr_g,locnum,&i);
    cur_set_itm(curr_g,i);
    return(curr_i);
    }
  if ((s) && (!strcmp(s,"*"))) {
    set_level(2);
    cur_set_itm(curr_g,ga[curr_g]->grp_count);
    return(curr_i);
    }
  return(0);
}

/*
 *  map_items
 *
 *  Map items into the mem array
 */

#if NNTP_CLIENT
static GRP_PTR sgap;
static unsigned int sgi;

/*+ V6.1B8 - 4-Sep-1993 - tmk - Use XOVER command to improve performance */
static int xoverni(cp1)
  char *cp1;
{
  char *cp3;
  int indx, i, j, x;
  char *xpitem, *xpsubj, *xpfrom, *xpdate, *xpmsid, *xprefs, *xpsize, *xpline;

  chop_str(cp1,'\n');
  if (sscanf(cp1,"%d",&indx) != 1) return 0;

  if (sgap->grp_count && find_itm_by_num(sgi,indx,&i)) return 0;
  else i = sgap->grp_count + 1;

  cp3 = cp1;

  xpitem=cp3;
  xpsubj=xpfrom=xpdate=xpmsid=xprefs=xpsize=xpline=NULL;
  xpsubj=strchr(cp3,'\t');
  if (xpsubj == NULL)
    err_line("NNTP XOVER error in Subject field.");
  else {
    *xpsubj='\0'; xpsubj++;
    xpfrom=strchr(xpsubj,'\t');
    if (xpfrom == NULL)
      err_line("NNTP XOVER error in From field.");
    else {
      *xpfrom='\0'; xpfrom++;
      xpdate=strchr(xpfrom,'\t');
      if (xpdate == NULL)
        err_line("NNTP XOVER error in Date field.");
      else {
        *xpdate='\0'; xpdate++;
        xpmsid=strchr(xpdate,'\t');
         if (xpmsid == NULL)
          err_line("NNTP XOVER error in Message-ID field.");
        else {
          *xpmsid='\0'; xpmsid++;
          xprefs=strchr(xpmsid,'\t');
          if (xprefs == NULL)
            err_line("NNTP XOVER error in References field.");
          else {
            *xprefs='\0'; xprefs++;
            xpsize=strchr(xprefs,'\t');
            if (xpsize == NULL)
              err_line("NNTP XOVER error in byte count field.");
            else {
              *xpsize='\0'; xpsize++;
              xpline=strchr(xpsize,'\t');
              if (xpline == NULL)
                err_line("NNTP XOVER error in Lines field.");
              else { *xpline='\0'; xpline++; }
            }
          }
        }
      }
    }
  }

  /* Note that we don't use the Message-ID or References fields. News will
   * grab the Message-ID's in the rare case that it needs them. Better to
   * avoid the overhead here.
   */

  if (sgap->grp_iasize == sgap->grp_count) {
    sgap->grp_iasize += 10;
    sgap->grp_ia = (ITM_PTR) news_realloc(sgap->grp_ia,(sgap->grp_iasize + 1) * (sizeof newsitm));
    }
  j = sgap->grp_count + 1;
  while (j > i) {
    sgap->grp_ia[j] = sgap->grp_ia[j-1];
    --j;
    }
  newsitm.itm_num = indx;
  newsitm.itm_grp = sgap->grp_num;
  *(newsitm.itm_id) = '\0';
  newsitm.itm_recvdate = 0;
  newsitm.itm_lines = 0;
  newsitm.itm_flags = NEWS_M_UNREAD;
  util_subjcpy(newsitm.itm_title,xpsubj);
  util_fromcpy(newsitm.itm_from,xpfrom);
  if (xpline  &&  (sscanf(xpline,"%d",&x) == 1) ) {
    newsitm.itm_lines = x;
    newsitm.itm_flags |= NEWS_M_LINESVALID;
  }
  newsitm.itm_postdate = newsitm.itm_recvdate =
    (xpdate ? parse_usenet_date(xpdate) : 0);
  newsitm.itm_cachedate = 0;
  newsitm.itm_life = 0;
  newsitm.itm_cid = 0;
  sgap->grp_ia[i] = newsitm;
  ++sgap->grp_count;
  return 1;
}
/*- V6.1B8 - 4-Sep-1993 - tmk - Use XOVER command to improve performance */

static int initni(cp1)
  char *cp1;
{
  char *cp3;
  int indx, i, j;

  chop_str(cp1,'\n');
  if (sscanf(cp1,"%d",&indx) != 1) return 0;

  if (sgap->grp_count && find_itm_by_num(sgi,indx,&i)) return 0;
  else i = sgap->grp_count + 1;

  cp3 = cp1;
  while (!isspace(*cp3)) ++cp3;
  while (isspace(*cp3)) ++cp3;

  if (sgap->grp_iasize == sgap->grp_count) {
    sgap->grp_iasize += 10;
    sgap->grp_ia = (ITM_PTR) news_realloc(sgap->grp_ia,(sgap->grp_iasize + 1) * (sizeof newsitm));
    }
  j = sgap->grp_count + 1;
  while (j > i) {
    sgap->grp_ia[j] = sgap->grp_ia[j-1];
    --j;
    }
  newsitm.itm_num = indx;
  newsitm.itm_grp = sgap->grp_num;
  *(newsitm.itm_id) = '\0';
  newsitm.itm_recvdate = 0;
  newsitm.itm_lines = 0;
  util_subjcpy(newsitm.itm_title,cp3);
  newsitm.itm_cachedate = 0;
  newsitm.itm_life = 0;
  newsitm.itm_flags = NEWS_M_UNREAD;
  newsitm.itm_cid = 0;
  sgap->grp_ia[i] = newsitm;
  ++sgap->grp_count;
  return 1;
}
#endif

int map_items(g)
  unsigned int g;
{
  int status;
  unsigned int key[2];
  GRP_PTR gap;
  ITM_PTR l_ia;
  char *itm_list, *bufptr, *cp1, *cp2, *cp3, subject[256];
  int f_itm, l_itm, i, j, count, first, last, indx, xhdr_call = 0, loc_disp_us,
      loc_disp_ui, loc_disp_stk;

  if ((!g) || (ga[g]->grp_ia)) return 0;
  loc_disp_us = display_unseen_stack;
  loc_disp_stk = display_stk;
  loc_disp_ui = display_unseen_items;
  gap = ga[g];
  if (gap->grp_flags & NEWS_M_NONDEFDISPLAY) {
    loc_disp_us = gap->grp_flags & NEWS_M_UNSEENSTACK;
    if (loc_disp_us) loc_disp_stk = gap->grp_displaystk;
    loc_disp_ui = gap->grp_flags & NEWS_M_UNREADITEMS;
    }
  if (loc_disp_ui) {
    loc_disp_us = 1;
    loc_disp_stk = 0;
    }

  if (gap->grp_flags & NEWS_M_MAILGROUP) return(mailmap(g));

  if (nntp_client) {
    *nntp_currgroup = '\0';
    sprintf(err_oline,"GROUP %s",gap->grp_name);
    if (nntp_one_call(nntp_node,nntp_proto,err_oline,&bufptr) == 211) {
      count = first = last = 0;
      sscanf(bufptr,"211 %d %d %d",&count,&first,&last);
      if (smg_active && gap->grp_display_indx) {
        sprintf(err_oline,"%5d",count);
        smg_put_chars(grp_vd,err_oline,gap->grp_display_indx,14+SUBJLEN,0,0);
        }
      if (count && loc_disp_us) {
        int funr;

        if (gap->grp_funread < first) gap->grp_funread = first;
        funr = max(gap->grp_funread,first) - loc_disp_stk;
        if (funr > first) first = funr;
        if (first > last) count = 0;
        }
      gap->grp_count = 0;
      gap->grp_iasize = count + 2;
      gap->grp_ia = (ITM_PTR) news_malloc((gap->grp_iasize + 1) * (sizeof newsitm));
      if (count) {
        sgap = gap;
        sgi = g;
/*+ V6.1B8 - 4-Sep-1993 - tmk - Use XOVER command to improve performance */
	sprintf(err_oline,"XOVER %d-%d", first,last);
        if ((NNTP_USE_XOVER) &&
            (nntp_get_info(nntp_node,nntp_proto,err_oline,224,0,xoverni))) {
          xhdr_call = 1;
          }
/*- V6.1B8 - 4-Sep-1993 - tmk - Use XOVER command to improve performance */
        else {
	  sprintf(err_oline,"XHDR Subject %d-%d",first,last);
          if ((NNTP_USE_XHDR) &&
              (nntp_get_info(nntp_node,nntp_proto,err_oline,221,0,initni))) {
            if (profile_display_lines) ds_lines(g);
            if (profile_display_postmark) ds_postmark(g);
            if (minfromlen > 0) ds_from(g);
            xhdr_call = 1;
          }
          else {
            for (indx = first; indx <= last; ++indx) {
              sprintf(err_oline,"HEAD %d",indx);
              if (nntp_call(nntp_node,nntp_proto,err_oline,&bufptr) == 221) {
                newsitm.itm_grp = gap->grp_num;
                util_subjcpy(newsitm.itm_title,"--Title not retrieved--");
                newsitm.itm_recvdate = 0;
                newsitm.itm_lines = 0;
                newsitm.itm_cachedate = 0;
                newsitm.itm_life = 0;
                newsitm.itm_flags = NEWS_M_UNREAD;	/* glass@mgi.com  V6.0-3  4/2/91 */
                newsitm.itm_cid = 0;
	        cp1 = chop_str_plus(bufptr,'\n');
                sscanf(bufptr,"221 %d %s",&newsitm.itm_num,subject);
                util_idcpy(newsitm.itm_id,subject);
                while (cp1) {
	  	  cp2 = chop_str_plus(cp1,'\n');
		  if ((cp3 = chop_str_plus(cp1,':')) != 0) {
                    while (isspace(*cp3)) ++cp3;
                    lower_case(cp1);
                    if (!strcmp(cp1,"subject")) util_subjcpy(newsitm.itm_title,cp3);
                    else if (!strcmp(cp1,"from")) util_fromcpy(newsitm.itm_from,cp3);
                    else if (!strcmp(cp1,"message-id")) util_idcpy(newsitm.itm_id,cp3);
                    else if (!strcmp(cp1,"lines")) {
                      if (sscanf(cp3,"%d",&(newsitm.itm_lines)) == 1)
                        newsitm.itm_flags |= NEWS_M_LINESVALID;
                      }
                    else if (!strcmp(cp1,"date"))
                      newsitm.itm_postdate = newsitm.itm_recvdate = parse_usenet_date(cp3);
		    }
                  cp1 = cp2;
                  }
                if (gap->grp_iasize == gap->grp_count) {
                  gap->grp_iasize += 10;
                  gap->grp_ia = (ITM_PTR) news_realloc(gap->grp_ia,(gap->grp_iasize + 1) * (sizeof newsitm));
                  }
                gap->grp_ia[++gap->grp_count] = newsitm;
                }
              }
	    }
          }
        }
      if (gap->grp_count)
        gap->grp_topnum = gap->grp_ia[gap->grp_count].itm_num;
      else gap->grp_topnum = last;
      }
    }
  else {        /*  !nntp_client  */
    key[0] = 0;
    if (loc_disp_us) {
      int funr = gap->grp_funread - loc_disp_stk;
      if (funr > 0) key[0] = funr;
      }
    if (!key[0]) gap->grp_iasize = gap->grp_count + 2;
    else gap->grp_iasize = min(gap->grp_count,
                               gap->grp_topnum - min(key[0],gap->grp_topnum)) + 2;
    gap->grp_ia = (ITM_PTR) news_malloc((gap->grp_iasize + 1) * (sizeof newsitm));
    gap->grp_count = 0;

    if (key[0] > gap->grp_topnum) {
      gap->grp_unread = 0;
      screen_update_gread(g);
      return 0;
    }

    key[1] = gap->grp_num;
    itmrab.rab$l_kbf = (char *) key;
    itmrab.rab$b_ksz = 8;
    itmrab.rab$b_krf = 0;
    itmrab.rab$l_rop = RAB$M_KGE | RAB$M_RRL | RAB$M_NLK;
    itmrab.rab$b_rac = RAB$C_KEY;

    while (sys_get_nornf(&itmrab)) {
      if (newsitm.itm_grp != key[1]) {
	if (gap->grp_count) {
	  if (gap->grp_ia[gap->grp_count].itm_num > gap->grp_topnum)
	    gap->grp_topnum = gap->grp_ia[gap->grp_count].itm_num;
	  }
	break;
	}
      itmrab.rab$b_rac = RAB$C_SEQ;
      itmrab.rab$l_rop = RAB$M_RRL | RAB$M_NLK;
      if (gap->grp_count >= gap->grp_iasize) {
        gap->grp_iasize += 10;
        gap->grp_ia = (ITM_PTR) news_realloc(gap->grp_ia,(gap->grp_iasize + 1) * (sizeof newsitm));
        }
      newsitm.itm_flags &= ~NEWS_M_NOACCESS;
      if (!(gap->grp_flags & NEWS_M_NNTPSRV))
        newsitm.itm_flags |= NEWS_M_LINESVALID;
      newsitm.itm_flags |= NEWS_M_UNREAD;
      newsitm.itm_title[SUBJLEN-1] = '\0';	/* temporary */
      newsitm.itm_id[IDLEN-1] = '\0';		/* temporary */
      gap->grp_ia[++gap->grp_count] = newsitm;
      }
    }
  gap->grp_unread = gap->grp_count;
  l_ia = gap->grp_ia;
  if ((itm_list = gap->grp_reg_text) != 0) {
    while (itm_list) {
/*  Do we ever see Unix-style .newsrc lists in ANU? Not AFAIK.  If we do,
 *  lots of code elsewhere will break, since it assumes ANU_style NewsRC.
 *  lists.  15-Jan-1995  Charles Bailey  bailey@genetics.upenn.edu
      char *cp, *cp1;

      if ((cp = strchr(itm_list,',')) != 0) {
        while (itm_list) {
	  cp = chop_str_plus(itm_list,',');
          if ((cp1 = strchr(itm_list,'-')) != 0) {
            sscanf(itm_list,"%d-%d",&f_itm,&l_itm);
            for (i = f_itm; i <= l_itm; ++i)
              if (find_itm_by_num(g,i,&j)) l_ia[j].itm_flags &= ~NEWS_M_UNREAD;
            }
	  else {
            sscanf(itm_list,"%d",&f_itm);
            if (find_itm_by_num(g,f_itm,&i)) l_ia[i].itm_flags &= ~NEWS_M_UNREAD;
	    }
          itm_list = cp;
          }
        }
      else 
 */
      if ((*itm_list == '<') || (*itm_list == '|')) {
        if (*itm_list == '|') {
          sscanf(++itm_list,"%d",&f_itm);
          itm_list = strchr(itm_list,'=');
          }
        else f_itm = max(ga[g]->grp_firstnum,1);
        sscanf(++itm_list,"%d",&l_itm);
        itm_list = strchr(itm_list,' ');
        if (itm_list) ++itm_list;
        for (i = f_itm; i <= l_itm; ++i)
          if (find_itm_by_num(g,i,&j)) l_ia[j].itm_flags &= ~NEWS_M_UNREAD;
        }
      else {
        sscanf(itm_list,"%d",&f_itm);
        itm_list = strchr(itm_list,' ');
        if (itm_list) ++itm_list;
        if (find_itm_by_num(g,f_itm,&i)) l_ia[i].itm_flags &= ~NEWS_M_UNREAD;
        }
      }
    if (loc_disp_ui) {
      for (i = 1; i <= gap->grp_count; ++i) {
        if (!(l_ia[i].itm_flags & NEWS_M_UNREAD)) {
          int j = i + 1;

          while (j <= gap->grp_count) {
            l_ia[j-1] = l_ia[j];
            ++j;
            }
          --(gap->grp_count);
          }
        }
      }
    gap->grp_unread = gap->grp_funread = 0;
    gap->grp_topreadnum = gap->grp_firstnum - 1;
    for (i = 1; i <= gap->grp_count; ++i) {
      if (l_ia[i].itm_flags & NEWS_M_UNREAD) {
        ++(gap->grp_unread);
        if (!gap->grp_funread || (l_ia[i].itm_num < gap->grp_funread))
          gap->grp_funread = l_ia[i].itm_num;
        }
      else {
        if (l_ia[i].itm_num > gap->grp_topreadnum)
          gap->grp_topreadnum = l_ia[i].itm_num;
        }
      }
    if (!gap->grp_funread) gap->grp_funread = gap->grp_topnum + 1;
    news_free(gap->grp_reg_text);
    gap->grp_reg_text = 0;
    }
  if ((gap->grp_flags & NEWS_M_ORDERBYSUBJECT) && gap->grp_count) {
    int i, j, k;
    ITM_PTR tmp, ttmp;
    char tmp_title[SUBJLEN], title[SUBJLEN], *cp;

    tmp = (ITM_PTR) news_malloc((gap->grp_iasize + 1) * (sizeof newsitm));
    j = 0;
    for (i = 1; i <= gap->grp_count; ++i) {
      if (!(gap->grp_ia[i].itm_num)) continue;
      tmp[++j] = gap->grp_ia[i];
      if (i == gap->grp_count) break;
      gap->grp_ia[i].itm_num = 0;
      strcpy(title,tmp[j].itm_title);
      lower_case(title);
      cp = title;
      while (!strncmp(cp,"re:",3)) {
        cp += 3;
    	while ((*cp) && (isspace(*cp))) cp++;
    	}
      if (!*cp) continue;
      strip(cp,strlen(cp));
      if (strlen(cp) > 20) cp[20] = '\0';
      for (k = i + 1; k <= gap->grp_count; ++k) {
        if (!(gap->grp_ia[k].itm_num)) continue;
        strcpy(tmp_title,gap->grp_ia[k].itm_title);
        lower_case(tmp_title);
        if (substrcmp(tmp_title,cp)) {
	  tmp[++j] = gap->grp_ia[k];
          gap->grp_ia[k].itm_num = 0;
          }
        }
      }
    ttmp = gap->grp_ia;
    gap->grp_ia = tmp;
    news_free(ttmp);
    }
  screen_update_gread(g);
  return 0;
}

#if NNTP_CLIENT
static int nntp_get_linecount(cp1)
  char *cp1;
{
  int indx, line_count, i;

  chop_str(cp1,'\n');
  if (sscanf(cp1,"%d %d",&indx,&line_count) != 2) return 0;

  if (find_itm_by_num(sgi,indx,&i)) {
    sgap->grp_ia[i].itm_lines = line_count;
    sgap->grp_ia[i].itm_flags |= NEWS_M_LINESVALID;
    if (sgap->grp_iavdsize) splashline(&(sgap->grp_ia[i]),sgap->grp_iavd,i);
    }
  return 1;
}
#endif

int ds_lines(g)
  int g;
{
  char *bufptr;
  int count, first, last;

  if (   !g
      || !smg_active
      || !nntp_client
      || !ga[g]->grp_count
      || !ga[g]->grp_ia
      || ga[g]->grp_flags & NEWS_M_MAILGROUP)
    return(0);

#if NNTP_CLIENT
  *nntp_currgroup = '\0';
  sprintf(err_oline,"GROUP %s",ga[g]->grp_name);
  if (nntp_one_call(nntp_node,nntp_proto,err_oline,&bufptr) == 211) {
    count = first = last = 0;
    sscanf(bufptr,"211 %d %d %d",&count,&first,&last);
    sgap = ga[g];
    sgi = g;
    if (count && sgap->grp_count) {
      sprintf(err_oline,"XHDR Lines %d-%d",
	sgap->grp_ia[1].itm_num,
	sgap->grp_ia[sgap->grp_count].itm_num);
      if (!(NNTP_USE_XHDR) || !(nntp_get_info(nntp_node,nntp_proto,err_oline,221,0,nntp_get_linecount))) {
        err_line("NNTP XHDR command not supported on the server");
        }
      }
    }
#endif
  return(0);
}

int do_show_linecounts()
{
  if (   !curr_g
      || news_context <= 1
      || !smg_active
      || !nntp_client
      || !ga[curr_g]->grp_count
      || ga[curr_g]->grp_flags & NEWS_M_MAILGROUP)
    return(0);
  ds_lines(curr_g);
  return(0);
}

#if NNTP_CLIENT
static int nntp_get_from(cp1)
  char *cp1;
{
  int indx, i;

  chop_str(cp1,'\n');
  if (sscanf(cp1,"%d",&indx) != 1) return 0;
  while (isdigit(*cp1)) cp1++;
  while (isspace(*cp1)) cp1++;

  if (find_itm_by_num(sgi,indx,&i)) {
    util_fromcpy(sgap->grp_ia[i].itm_from,cp1);
    if (sgap->grp_iavdsize) splashline(&(sgap->grp_ia[i]),sgap->grp_iavd,i);
    }
  return 1;
}
#endif

int ds_from(g)
  int g;
{
  char *bufptr;
  int count, first, last;
  if (   !g
      || !smg_active
      || !nntp_client
      || !ga[g]->grp_count
      || !ga[g]->grp_ia
      || ga[g]->grp_flags & NEWS_M_MAILGROUP)
    return(0);

#if NNTP_CLIENT
  *nntp_currgroup = '\0';
  sprintf(err_oline,"GROUP %s",ga[g]->grp_name);
  if (nntp_one_call(nntp_node,nntp_proto,err_oline,&bufptr) == 211) {
    count = first = last = 0;
    sscanf(bufptr,"211 %d %d %d",&count,&first,&last);
    sgap = ga[g];
    sgi = g;
    if (count && sgap->grp_count) {
      sprintf(err_oline,"XHDR From %d-%d",
	sgap->grp_ia[1].itm_num,
	sgap->grp_ia[sgap->grp_count].itm_num);
      if (!(NNTP_USE_XHDR) || !(nntp_get_info(nntp_node,nntp_proto,err_oline,221,0,nntp_get_from))) {
        err_line("NNTP XHDR command not supported on the server");
        }
      }
    }
#endif
  return(0);
}

static int nntp_get_postmark(cp1)
  char *cp1;
{
  int indx, postmark, i;

  chop_str(cp1,'\n');
  if (sscanf(cp1,"%d %d",&indx,&postmark) != 2) return 0;

  if (find_itm_by_num(sgi,indx,&i)) {
    sgap->grp_ia[i].itm_recvdate = postmark;
    if (sgap->grp_iavdsize) splashline(&(sgap->grp_ia[i]),sgap->grp_iavd,i);
    }
  return 1;
}

static int nntp_get_date(cp1)
  char *cp1;
{
  char *cp2;
  int indx = 0, postmark, i;

  chop_str(cp1,'\n');
  cp2 = cp1;
  while (isspace(*cp2)) ++cp2;
  while (isdigit(*cp2)) indx = (10 * indx) + *cp2++ - '0';
  while (isspace(*cp2)) ++cp2;
  postmark = parse_usenet_date(cp2);

  if (!indx || !postmark) return 0;

  if (find_itm_by_num(sgi,indx,&i)) {
    sgap->grp_ia[i].itm_recvdate = postmark;
    if (sgap->grp_iavdsize) splashline(&(sgap->grp_ia[i]),sgap->grp_iavd,i);
    }
  return 1;
}

int ds_postmark(g)
  int g;
{
  char *bufptr;
  int count, first, last;

  if (   !g
      || !smg_active
      || !nntp_client
      || !ga[g]->grp_count
      || !ga[g]->grp_ia
      || ga[g]->grp_flags & NEWS_M_MAILGROUP)
    return(0);

  *nntp_currgroup = '\0';
  sprintf(err_oline,"GROUP %s",ga[g]->grp_name);
  if (nntp_one_call(nntp_node,nntp_proto,err_oline,&bufptr) == 211) {
    count = first = last = 0;
    sscanf(bufptr,"211 %d %d %d",&count,&first,&last);
    sgap = ga[g];
    sgi = g;
    if (count && sgap->grp_count) {
      if (nntp_anu_news_server) {
        sprintf(err_oline,"XHDR Postmark-Local %d-%d",
	  sgap->grp_ia[1].itm_num,
	  sgap->grp_ia[sgap->grp_count].itm_num);
        if (!(NNTP_USE_XHDR) || !nntp_get_info(nntp_node,nntp_proto,err_oline,221,0,nntp_get_postmark)) {
          err_line("NNTP XHDR command not supported on the server");
          }
        }
      else {
        sprintf(err_oline,"XHDR Date %d-%d",
	  sgap->grp_ia[1].itm_num,
	  sgap->grp_ia[sgap->grp_count].itm_num);
        if (!(NNTP_USE_XHDR) || !nntp_get_info(nntp_node,nntp_proto,err_oline,221,0,nntp_get_date)) {
          err_line("NNTP XHDR command not supported on the server");
          }
        }
      }
    }
  return(0);
}

int do_show_postmarks()
{
  if (   !curr_g
      || news_context <= 1
      || !smg_active
      || !nntp_client
      || !ga[curr_g]->grp_count
      || ga[curr_g]->grp_flags & NEWS_M_MAILGROUP)
    return(0);
  ds_postmark(curr_g);
  return(0);
}

#if NNTP_CLIENT
/*
 *  map_ids - fill in message-id fields in ia and/or id-itmnum map list
 *            This will only do real work if we're an nntp client and
 *            using XHDR commands, otherwise the id fields were filled in
 *            by map_items(), and we'll simply return here.
 */

static int map_g;

static int additmid(xhdr_line)
  char *xhdr_line;
{
  int itmnum,iidx;
  char *id;

  chop_str(xhdr_line,'\n');
  if (sscanf(xhdr_line,"%d",&itmnum) != 1) return(0);
  if (!(id = strchr(xhdr_line,'>'))) return(0);
  *(++id) = '\0';
  if (!(id = strchr(xhdr_line,'<'))) return(0);
  if (find_itm_by_num(map_g,itmnum,&iidx))
    util_idcpy(ga[map_g]->grp_ia[iidx].itm_id,id);
  else {
    IDMAP_PTR tmp;

    if (!(tmp = (IDMAP_PTR) news_malloc(sizeof(IDMAP)))) {
      sprintf(err_oline,"news_malloc() failure expanding Message-ID list");
      err_line(err_oline);
      return(0);
      }
    tmp->itm_num = itmnum;
    tmp->f_link = 0;
    util_idcpy(tmp->id,id);
    if (!ga[map_g]->grp_idhead) {
      tmp->b_link = 0;
      ga[map_g]->grp_idhead = tmp;
      ga[map_g]->grp_idtail = tmp;
      }
    else {
      (ga[map_g]->grp_idtail)->f_link = tmp;
      tmp->b_link = ga[map_g]->grp_idtail;
      ga[map_g]->grp_idtail = tmp;
      }
    }
  return(1);
}

int map_ids(g)
  unsigned int g;
{
  char *nntp_line;

  if (!ga[g]->grp_ia) map_items(g);
  if (!ga[g]->grp_ia) return(0);
  if (ga[g]->grp_flags & NEWS_M_ITMIDSSET) return(1);

#if NNTP_USE_XHDR
  /* we should only get this far if we're an nntp client and we used the
     XHDR command in map_items() */
  if (strcmp(ga[g]->grp_name,nntp_currgroup)) {
    sprintf(err_oline,"GROUP %s",ga[g]->grp_name);
    if (nntp_one_call(nntp_node,nntp_proto,err_oline,&nntp_line) != 211)
      return(0);
    strcpy(nntp_currgroup,ga[g]->grp_name);
    }
  map_g = g;
  sprintf(err_oline,"XHDR message-id %d-%d",ga[g]->grp_firstnum,
          ga[g]->grp_topnum);
  if (!nntp_get_info(nntp_node,nntp_proto,err_oline,221,0,additmid)) {
    sprintf(err_oline,"NNTP XHDR error reading message ids");
    err_line(err_oline);
    return(0);
    }
  ga[g]->grp_flags |= NEWS_M_ITMIDSSET;
#endif /* NNTP_USE_XHDR */

  return(1);
}
#endif /* NNTP_CLIENT */

/*
 *  mem_reset
 *
 *  reset the memory status to match the current file status
 */

void
mem_reset(cd,write_reg)
  int cd, write_reg;
{
  int status;
  int g,
      num_grps,
      mfo = mailfile_open,
      ssa;


  if (mfo) do_close_mail();
  set_level(1);
  ssa = smg_active;
  if (write_reg) write_reg_file();

  for (g = 1; g <= ga_size; ++g) {
    if (ga[g]->grp_ia) news_free(ga[g]->grp_ia);
    if (ga[g]->grp_iavdsize)
      _c$cks(smg$delete_virtual_display(&ga[g]->grp_iavd));
    if (ga[g]->grp_idtail) {
      while ((ga[g]->grp_idtail = (ga[g]->grp_idtail)->b_link))
        news_free((ga[g]->grp_idtail)->f_link);
      news_free(ga[g]->grp_idhead);
      }
    news_free(ga[g]);
    }
  news_free(ga);

  curr_g = 0;
  open_groups(cd);
  if (gv_size) {
    num_grps = ga[0]->grp_count;
    if (num_grps > gv_size)
      c$cks(smg$change_virtual_display(&grp_vd,&num_grps,c$ac(devcol)));
    gv_size = num_grps;
    }
  if (ssa && !screen_displays_setup) {
    smg_active = 0;
    init_screen();
    }
  else if (smg_active) screen_map_dir();
  no_more_news = 0;
  if (mfo) do_open_mail();
}

/*
 *  noscreen
 *
 *  Revert to line mode interface
 */

int noscreen()
{
    int g;
    int status;

    if (smg_active) {
        _c$cks(smg$begin_pasteboard_update(&pid));
        no_broad_trap();
        set_level(1);
        for (g = 1; g <= ga_size; ++g) {
            if (ga[g]->grp_iavdsize) {
                _c$cks(smg$delete_virtual_display(&ga[g]->grp_iavd));
                ga[g]->grp_iavdsize = 0;
                }
            }
        _c$cks(smg$delete_virtual_display(&grp_vd));
        gv_size = 0;
        _c$cks(smg$delete_virtual_display(&grp_header_vd));
        _c$cks(smg$delete_virtual_display(&itm_header_vd));
        _c$cks(smg$delete_virtual_display(&trailer_vd));
        _c$cks(smg$delete_virtual_display(&(itmptr.sdid)));
        _c$cks(smg$delete_virtual_display(&(itmptr.shdid)));
        _c$cks(smg$delete_virtual_display(&(textptr.sdid)));
        _c$cks(smg$delete_virtual_display(&(textptr.shdid)));
        while (smg$end_pasteboard_update(&pid) == SMG$_BATSTIPRO);
        reset_mode();
        smg_active = 0;
        screen_displays_setup = 0;
        }
    return(0);
}

int do_noscreen()
{
  return(unwind_display(OUTER_LOOP,noscreen));
}

/*
 *  position_display
 *
 *  reposition the virtual display vd (currently pasted at *cp) to display
 *  line p
 */

void
position_display(vd,cp,p,refresh,maxsize)
    int *vd,
        *cp,
        p,
        refresh,
        maxsize;
{
    int status;

    if (p <= 7) {
        if (*cp == 4) return;
        *cp = 4;	/* display 1st line */
        }
    else if (p > (maxsize - 7)) {
        if (*cp <= ((devrow - 2) - maxsize)) return;
        *cp = ((devrow - 2) - maxsize);	    /* display last line */
        }
    else {
        if ((*cp >= (11 - p)) && (*cp <= ((devrow - 8) - p))) return;
        if (*cp < (11 - p)) *cp = (11 - p);
        else *cp = ((devrow - 8) - p);
        }
/* OLD CODE-------------------------
    if ((maxsize - p < 7) || (p < 7)) {
        if ((p >= (5 - *cp)) && (p <= (devrow - (2 + *cp)))) return;
        if (p < (5 - *cp)) *cp = (5 - p);
        else *cp = (devrow - (2 + p));
        }
    else {
        if ((p >= (11 - *cp)) && (p <= (devrow - (9 + *cp)))) return;
        if (p < (11 - *cp)) *cp = (11 - p);
        else *cp = (devrow - (9 + p));
        }
----------------------------------------*/
    if (refresh) c$cks(smg$end_pasteboard_update(&pid));
    _c$cks(smg$move_virtual_display(vd,&pid,cp,c$ac(1),0));
    if (refresh) c$cks(smg$begin_pasteboard_update(&pid));
}

/*
 *  screen_map_dir
 *
 *  Redraw the group level screen virtual display
 */

void
screen_map_dir()
{
  int g,
      sg = 0;
  int status;
  char dir_type[120];

  g_arrow = 0;
  _c$cks(smg$erase_display(&grp_vd));
  if ((!curr_g) && ga_size) curr_g = 1;

  for (g = 1; g <= ga_size; ++g) {
    if (ga[g]->grp_display_indx) {
      ga[g]->grp_display_indx = ++sg;

      /* the following screen resizing was added to prevent writing beyond
         virtual screen size during operation as a client when in FASTLOAD mode
         and when DIR/ALL is selected.  Unfortunately I lack the thorough
         understanding of grp_vd size handling and this is just a good estimate.
           mark.martinec@ijs.si
       */
      if ((gv_size) && (sg > gv_size)) {
        gv_size = ga_size; /* bump to full size and reduce at the end of loop */
        _c$cks(smg$change_virtual_display(&grp_vd,&gv_size,c$ac(devcol),0,0,0));
        }

      if (nntp_client && ga[g]->grp_firstnum < 1)
          sprintf(err_oline,"   %-5d %c %-*.*s           %5d",sg,
              ((ga[g]->grp_flags & NEWS_M_NNTPSRV)? '_' : ' '),SUBJLEN,SUBJLEN,
              ga[g]->grp_name,ga[g]->grp_unread);
      else sprintf(err_oline,"   %-5d %c %-*.*s  %5d    %5d",sg,
              ((ga[g]->grp_flags & NEWS_M_NNTPSRV)? '_' : ' '),SUBJLEN,SUBJLEN,
              ga[g]->grp_name,ga[g]->grp_count,ga[g]->grp_unread);
      smg_put_chars(grp_vd,err_oline,sg,1,0,0);
      if (g == curr_g) {
        g_arrow = sg;
        smg_put_chars(grp_vd,"->",sg,1,0,SMG$M_REVERSE+SMG$M_BOLD);
        }
      if (ga[g]->grp_reg) {
        _c$cks(smg$change_rendition(&grp_vd,&sg,c$ac(12),c$ac(1),c$ac(SUBJLEN),c$ac(SMG$M_BOLD),0));
        if (ga[g]->grp_unread)
          _c$cks(smg$change_rendition(&grp_vd,&sg,c$ac(4),c$ac(1),c$ac(5),c$ac(SMG$M_BOLD),0));
        }
      }
    }
  grp_display_size = sg;
  if ((gv_size) && (gv_size > sg+10)) {  /* reduce if significantly too large */
    gv_size = max(1,sg);       /* sg could be 0, which is illegal window size */
    _c$cks(smg$change_virtual_display(&grp_vd,&gv_size,c$ac(devcol),0,0,0));
    }
  if (cur_dir_type == DIR_DATE) sprintf(dir_type,"NEWSGROUPS %s [SINCE=%s, %d Newsgroups]",class_name(),dir_sincestr,sg);
  else if (cur_dir_type == DIR_NEW) sprintf(dir_type,"NEWSGROUPS %s [NEW, %d Newsgroups]",class_name(),sg);
  else if (cur_dir_type == DIR_REGISTER)  sprintf(dir_type,"NEWSGROUPS %s [REGISTERED, %d Newsgroups]",class_name(),sg);
  else if (cur_dir_type == DIR_UNREGISTER)  sprintf(dir_type,"NEWSGROUPS %s [UNREGISTERED, %d Newsgroups]",class_name(),sg);
  else sprintf(dir_type,"NEWSGROUPS %s [ALL, %d Newsgroups]",class_name(),sg);
  smg_put_chars(grp_header_vd,dir_type,1,4,1,0);

  if ((!g_arrow) || (grp_display_size <= devrow - 6)) grp_paste = 4;
  else {
    grp_paste = 11 - g_arrow;
    if (grp_paste > 4) grp_paste = 4;
    if (grp_paste < ((devrow - 2) - grp_display_size)) grp_paste = (devrow - 2) - grp_display_size;
    }
  _c$cks(smg$move_virtual_display(&grp_vd,&pid,&grp_paste,c$ac(1),0));
}

void newsgroup_status_line(g)
  unsigned int g;
{
  GRP_PTR gap = ga[g];
  char itemhold[50];

  gwrite_access(g);
  *err_oline = '\0';
  if (gap->grp_reg) {
    sprintf(itemhold,"Reg[002]:[111]%u",gap->grp_reg);
    strcat(err_oline,itemhold);
    }

  if (gap->grp_flags & NEWS_M_RESTRICT_SET) strcat(err_oline,"[161]Restrict[007]");

  if (!nntp_client) {
    strcat(err_oline,"[161]Prot[003]:[111]R");
    if (gap->grp_flags & NEWS_M_WRITE_ACCESS) strcat(err_oline,"W");
    if (gap->grp_flags & NEWS_M_MOD_ACCESS) strcat(err_oline,"M");
    else if (gap->grp_flags & NEWS_M_MOD_USER) strcat(err_oline,"m");
    if (!no_priv()) strcat(err_oline,"X");
    }

  if (nntp_client) {
    strcat(err_oline,"[161]Server[005]:[111]");
    strcat(err_oline,nntp_node);
    }
  else if (ga[g]->grp_flags & NEWS_M_NNTPSRV) {
    strcat(err_oline,"[161]Server[005]:[111]");
    if (gap->grp_srvproto) strcat(err_oline,"@");
    strcat(err_oline,gap->grp_srvnode);
    if (!gap->grp_srvproto) strcat(err_oline,"::");
    if (gap->grp_flags & NEWS_M_NNTPCACHE)
      sprintf(&err_oline[strlen(err_oline)],",[161](cache[004]:[111]%d)",gap->grp_srvcache);
    }
}

void newsgroup_header()
{
  char tmp[IO_SIZE];
  GRP_PTR gap = ga[curr_g];
  ITM_PTR iap;
  int status;

  newsgroup_status_line(curr_g);
  if (!gap->grp_ia) map_items(curr_g);
  iap = gap->grp_ia;
  if (gap->grp_count)
    sprintf(tmp,"%s:[171] %d Items[161](#%d - #%d)[171]%s",gap->grp_name,gap->grp_count,
        iap[1].itm_num,iap[gap->grp_count].itm_num,err_oline);
  else sprintf(tmp,"%s:[171] %d Items[171]%s",gap->grp_name,gap->grp_count,err_oline);
  fit_line(tmp, -1, sizeof tmp, (devcol?devcol:80));
  smg_put_chars(itm_header_vd,tmp,1,1,1,SMG$M_BOLD);
  if (*(gap->grp_topic)) {
    sprintf(tmp,"        -<%s>-",gap->grp_topic);
    smg_put_chars(itm_header_vd,tmp,2,1,1,SMG$M_BOLD);
    }
  else _c$cks(smg$erase_line(&itm_header_vd,c$ac(2),c$ac(1)));
  _c$cks(smg$erase_line(&itm_header_vd,c$ac(3),c$ac(1)));
  if (*(gap->grp_notice)) {
    sprintf(tmp,"         [Notice: %s]",gap->grp_notice);
    smg_put_chars(itm_header_vd,tmp,3,1,1,SMG$M_BOLD);
    }
  else {
    int li, ri, i;

    smg_put_chars(itm_header_vd,"      Title",3,4,0,0);
    if (minfromlen > 0 && !v59_file && devcol >= 80) {
      li = (67 - min(minfromlen,devcol - 30)) + (devcol - 80);
      ri = SUBJLEN + 9;
      i = min(li,ri);
      smg_put_chars(itm_header_vd," From",3,i,0,0);
      li = devcol - 6;
      ri = i + minfromlen + 6;
      }
    else {
      li = devcol - 6;
      ri = SUBJLEN + 15;
      }
    i = max(li,ri);
    smg_put_chars(itm_header_vd,"  Date",3,i,0,0);
    i -= 6;
    smg_put_chars(itm_header_vd," Lines",3,i,0,0);
    }
}

/*
 *  set_level
 *
 *  Set the screen display level
 */

void set_level(l)
  int l;
{
  int i;
  GRP_PTR gap = ga[curr_g];
  ITM_PTR iap;
  int status;

  if (news_context == 3) {
    news_context = 2;
    if (smg_active) {
      smg_active = 2;
      if (itmptr.text_displayed) {
        _c$cks(smg$unpaste_virtual_display(&(itmptr.sdid),&pid));
        _c$cks(smg$unpaste_virtual_display(&(itmptr.shdid),&pid));
        itmptr.text_displayed = 0;
        }
      if (textptr.text_displayed) {
        _c$cks(smg$unpaste_virtual_display(&(textptr.sdid),&pid));
        _c$cks(smg$unpaste_virtual_display(&(textptr.shdid),&pid));
        textptr.text_displayed = 0;
        }
      }
    }
  if (news_context == l) return;
  if (l == 1) {
    news_context = 1;
    curr_i = -1;
    if (smg_active) {
      smg_active = 1;
      if ((!curr_g) || (!gap->grp_iavdsize)) return;
      _c$cks(smg$unpaste_virtual_display(&itm_header_vd,&pid));
      _c$cks(smg$unpaste_virtual_display(&(gap->grp_iavd),&pid));
      }
    return;
    }

  if (!curr_g) return;
  if (!gap->grp_count) {
    if (smg_active && (!gap->grp_iavdsize)) {
      _c$cks(smg$create_virtual_display(&devrow,c$ac(devcol),&(gap->grp_iavd),0,0,0));
      smg_put_chars(gap->grp_iavd,"NEWSGROUP is empty",1,20,0,SMG$M_REVERSE);
      gap->grp_iavdsize = devrow;
      gap->grp_iapaste = 4;
      }
    gap->grp_c_itm = 0;
    }
  else {
    if (!gap->grp_ia) map_items(curr_g);
    if (!gap->grp_count) {
      if (smg_active && (!gap->grp_iavdsize)) {
        _c$cks(smg$create_virtual_display(&devrow,c$ac(devcol),&(gap->grp_iavd),0,0,0));
        smg_put_chars(gap->grp_iavd,"NEWSGROUP is empty",1,20,0,SMG$M_REVERSE);
        gap->grp_iavdsize = devrow;
        gap->grp_iapaste = 4;
        }
      gap->grp_c_itm = 0;
      }
    else {
      iap = gap->grp_ia;
      if (smg_active && (!gap->grp_iavdsize)) {
        _c$cks(smg$create_virtual_display(c$rfi(gap->grp_iavdsize = max(devrow,gap->grp_count)),c$ac(devcol),&(gap->grp_iavd),0,0,0));
        for (i = 1; i <= gap->grp_count; ++i) {
          splashline(&(iap[i]),gap->grp_iavd,i);
          if (iap[i].itm_flags & NEWS_M_UNREAD) {
            if (!gap->grp_c_itm) {
              gap->grp_c_itm = i;
              smg_put_chars(gap->grp_iavd,"->",i,1,0,SMG$M_REVERSE+SMG$M_BOLD);
              }
            }
          }
        }
      if (!gap->grp_c_itm) {
        for (i = 1; i < gap->grp_count; ++i)
          if (iap[i].itm_flags & NEWS_M_UNREAD) break;
        gap->grp_c_itm = i;
        if (gap->grp_iavdsize)
          smg_put_chars(gap->grp_iavd,"->",i,1,0,SMG$M_REVERSE+SMG$M_BOLD);
        }
      }
    }
  curr_i = gap->grp_c_itm;
  if (smg_active) {

    gap->grp_iapaste = 4;
    if (curr_i) {
      position_display(&gap->grp_iavd,&gap->grp_iapaste,gap->grp_count,0,gap->grp_count);
      position_display(&gap->grp_iavd,&gap->grp_iapaste,curr_i,0,gap->grp_count);
      smg_put_chars(gap->grp_iavd,"->",curr_i,1,0,SMG$M_REVERSE+SMG$M_BOLD);
      }
    newsgroup_header();
    c$cks(smg$paste_virtual_display(&(gap->grp_iavd),&pid,&(gap->grp_iapaste),c$ac(1)));
    c$cks(smg$paste_virtual_display(&itm_header_vd,&pid,c$ac(1),c$ac(1)));
    c$cks(smg$repaste_virtual_display(&trailer_vd,&pid,c$rfi(devrow - 2),c$ac(1)));
    smg_active = 2;
    }
  news_context = 2;
}

/*
 *  util_cvrt
 *
 *  Convert a string into standard newsgroup format
 */

void util_cvrt(result, input)
    char *result,
         *input;
{
    char *p = result,
         *in = input;
    int i;

    while ((*in) && (*in == ' ')) in++;
    strncpy(result,in,SUBJLEN);
    result[SUBJLEN - 1] = '\0';
    strip(result,strlen(result));
    while (*p) {
        if (isgraph(*p))
#ifdef vax11c
	  *p = tolower(*p);
#else
	  {
          if (*p >= 'A' && *p <= 'Z')
             *p += 'a' - 'A' ;
          }
#endif
        else
          *p = '_';
        p++;
        }
    i = strlen(result);
    while (i < SUBJLEN) result[i++] = '\0';
}

/*
 *  util_dir
 *
 *  Convert a newsgroup name to a VMS directory string
 */

char dir_result[SUBJLEN + SUBJLEN];

char *
util_dir(input)
    const char *input;
{
    char *p = dir_result;
    const char *in = input;

    while (*in) {
        if (isalnum(*in) || (*in == '-') || (*in == '.')) *p++ = *in++;
        else {
            *p++ = '_';
            if (*in == '_') *p++ = '_';
            else if (*in < '0') *p++ = (*in - '!') + 'A';
            else if (*in < 'A') *p++ = (*in - ':') + '0';
            else if (*in < 'a') *p++ = (*in - '[') + 'P';
            else *p++ = (*in - '{') + 'V';
            in++;
            }
        }
    *p = '\0';
    return(dir_result);
}

/*
 *  util_undir
 *
 *  Convert a directory name to a newsgroup string
 */

char undir_result[SUBJLEN];

char *
util_undir(input)
    const char *input;
{
    char *p = undir_result;
    const char *in = input;

    while (*in) {
        if (*in == '_') {
            in++;
            if (*in < 'A') *p++ = (*in - '0') + ':';
            else if (*in < 'P') *p++ = (*in - 'A') + '!';
            else if (*in < 'V') *p++ = (*in - 'P') + '[';
            else *p++ = (*in - 'V') + '{';
            }
        else *p++ = tolower(*in);
        in++;
        }
    *p = '\0';
    return(undir_result);
}

/*
 *  strip
 *
 *  remove trailing blanks
 */

void
strip(p,n)
    char *p;
    int n;
{
    do {
        n--;
        } while ((n > 0) && (p[n] == ' '));
    p[n+1] = '\0';
}

/*
 *  do_refresh
 *
 *  rewrite the SMG screen
 */

int do_refresh()
{
  int status;

  if (smg_active) {
    _c$cks(smg$end_pasteboard_update(&pid));
    _c$cks(smg$repaint_screen(&pid));
    _c$cks(smg$begin_pasteboard_update(&pid));
    _c$cks(smg$set_keypad_mode(&kid,c$ac(SMG$M_KEYPAD_APPLICATION)));
    }
  return(0);
}

/*
 *  do_error
 *
 *  CLI problem
 */

int do_error()
{
  err_line("Error: News - unrecognised command");
  return(0);
}

/*
 *  do_exit
 *
 *  bye for now
 */

int do_exit()
{
    return(1);
}

/*
 *  do_quit
 *
 *  Close rms files, DONT write the register file, shutdown the screen, and
 *  send any printer output to a nominated queue.
 */

int do_quit()
{
    int status;

    err_line("QUIT will NOT save any REGISTER, READ, MARK, SKIP commands from this session");
    status = get_input(&usr_inp_dsc,c$dsc("Quit? [y]"),&usr_inp_l);
    if (!(status & 1)) return(1);
    if (!(!usr_inp_l || (*usr_inp == 'Y') || (*usr_inp == 'y'))) return(0);
    closefiles_opt(0,1);
    exit(1);
    /*NOTREACHED*/
    return 1;
}

/*
 *  screen_grp_display
 *
 *  NEWSGROUPS command - set the screen to the newsgroup directory
 */

int screen_grp_display()
{
    set_level(1);
    return(0);
}

int do_groups_display()
{
  return(unwind_display(OUTER_LOOP,screen_grp_display));
}

/*
 *  screen_cur_down
 *
 *  Move the cursor down
 */

int screen_cur_down()
{
  char line[132];
  unsigned short line_len;
  int lines;
  $DESCRIPTOR(line_dsc,line);

  if (cli$get_value(c$dsc("LINES"),&line_dsc,&line_len) & 1) {
    line[line_len] = '\0';
    if (sscanf(line,"%d",&lines) != 1) lines = 1;
    }
  else lines = 1;
  if (!lines) return(0);
  if (lines < 0) {
    if (news_context == 1) cur_up_grp(lines,1);
    else {
      set_level(2);
      cur_up_itm(curr_g,lines,1);
      }
    return(0);
    }
  if (news_context == 1) cur_down_grp(lines,1);
  else {
    set_level(2);
    cur_down_itm(curr_g,lines,1);
    }
  return(0);
}

/*
 *  screen_cur_up
 *
 *  move the cursor up by n lines on the screen
 */

int screen_cur_up()
{
    char line[132];
    unsigned short line_len;
    int lines;
    $DESCRIPTOR(line_dsc,line);

    if (cli$get_value(c$dsc("LINES"),&line_dsc,&line_len) & 1) {
        line[line_len] = '\0';
        if (sscanf(line,"%d",&lines) != 1) lines = 1;
        }
    else lines = 1;
    if (!lines) return(0);
    if (lines < 0) {
        if (news_context == 1) cur_down_grp(lines,1);
        else {
            set_level(2);
            cur_down_itm(curr_g,lines,1);
            }
        return(0);
        }
    if (news_context == 1) cur_up_grp(lines,1);
    else {
        set_level(2);
        cur_up_itm(curr_g,lines,1);
        }
    return(0);
}

int do_top()
{
  if (news_context == 1) cur_up_grp(1000000,1);
  else {
    set_level(2);
    cur_up_itm(curr_g,1000000,1);
    }
  return(0);
}

int do_bottom()
{
  if (news_context == 1) cur_down_grp(1000000,1);
  else {
    set_level(2);
    cur_down_itm(curr_g,1000000,1);
    }
  return(0);
}

int check_id(id)
  char *id;
{
  char mref[IDLEN + 4];
  int *gp;
  int status;

  util_idcpy(mref,id);
  gp = (int *)&mref[IDLEN];
  *gp = 0;

  itmrab.rab$l_kbf = mref;
  itmrab.rab$b_ksz = IDLEN + 4;
  itmrab.rab$b_krf = 1;
  itmrab.rab$l_rop = RAB$M_KGE | RAB$M_RRL | RAB$M_NLK;
  itmrab.rab$b_rac = RAB$C_KEY;

  if (sys_get_nornf(&itmrab) && !strcmp(mref,newsitm.itm_id)) return(1);
  if (hist_check(mref)) return(1);
  return(0);
}

int
do_noop()
{
  return(0);
}

/* unused function as far as I can tell (19-aUG-1994 mark.martinec@ijs.si) */
/*
 *static char *markoff = 0;
 *
 *static char *regtext_array(first,last,text)
 *  int first, last;
 *  char *text;
 *{
 *  int f_itm, l_itm,i;
 *  char *cp = text;
 *  int status;
 *
 *  if (markoff) news_free(markoff);
 *  markoff = 0;
 *  if (last < first) return(0);
 *  markoff = news_malloc((last - first) + 1);
 *  for (i = first; i <= last; ++i) markoff[i-first] = 1;
 *  if (!text || !*text) return(markoff);
 *  while (*cp) {
 *    if ((*cp == '<') || (*cp == '|')) {
 *      if (*cp == '|') {
 *        sscanf(++cp,"%d",&f_itm);
 *        cp = strchr(cp,'=');
 *        }
 *      else f_itm = 1;
 *      sscanf(++cp,"%d",&l_itm);
 *      cp = strchr(cp,' ');
 *      if (cp) ++cp;
 *      if (f_itm < first) f_itm = first;
 *      if (l_itm > last) l_itm = last;
 *      for (i = f_itm; i <= l_itm; ++i) markoff[i-first] = 0;
 *      }
 *    else {
 *      sscanf(cp,"%d",&f_itm);
 *      cp = strchr(cp,' ');
 *      if (cp) ++cp;
 *      if ((f_itm >= first) && (f_itm <= last)) markoff[f_itm - first] = 0;
 *      }
 *    }
 *  return(markoff);
 *}
 */

/* unused function as far as I can tell (19-aUG-1994 mark.martinec@ijs.si) */
/*
 *static char *ret_range = 0;
 *
 *static char *regtext_range(first,last,text)
 *  int first, last;
 *
 *  char *text;
 *{
 *  int i, f_itm = 0, l_itm = 0,rr_len = 0, rt_len = 0;
 *  char *mask, tmptxt[80];
 *  int status;
 *
 *  if (ret_range) news_free(ret_range);
 *  ret_range = 0;
 *  if (!(mask = regtext_array(first,last,text))) return(0);
 *
 *  for (i = first; i <= last; ++i) {
 *    if (!mask[i-first]) {
 *      if (f_itm) {
 *        if (l_itm == f_itm) sprintf(tmptxt,"%d",f_itm);
 *        else sprintf(tmptxt,"%d-%d",f_itm,l_itm);
 *        if ((rt_len + strlen(tmptxt) + 10) > rr_len) {
 *          if (!rr_len) ret_range = news_malloc(rr_len = 256);
 *          else ret_range = news_realloc(ret_range,rr_len += 256);
 *          }
 *        if (!*ret_range) strcpy(ret_range,tmptxt);
 *        else {
 *          strcat(ret_range," ");
 *          strcat(ret_range,tmptxt);
 *          }
 *        rt_len = strlen(ret_range);
 *        f_itm = l_itm = 0;
 *        }
 *      }
 *    else {
 *      if (!f_itm) f_itm = l_itm = i;
 *      else l_itm = 1;
 *      }
 *    }
 *  if (f_itm) {
 *    if (l_itm == f_itm) sprintf(tmptxt,"%d",f_itm);
 *    else sprintf(tmptxt,"%d-%d",f_itm,l_itm);
 *    if ((rt_len + strlen(tmptxt) + 10) > rr_len) {
 *      if (!rr_len) ret_range = news_malloc(rr_len = 256);
 *      else ret_range = news_realloc(ret_range,rr_len += 256);
 *      }
 *    if (!*ret_range) strcpy(ret_range,tmptxt);
 *    else {
 *      strcat(ret_range," ");
 *      strcat(ret_range,tmptxt);
 *      }
 *    }
 *  return(ret_range);
 *}
 */

int scdown()
{
  int lines = devrow - 6;

  if (news_context == 1) cur_down_grp(lines,1);
  else {
    set_level(2);
    cur_down_itm(curr_g,lines,1);
    }
  return(0);
}

int scup()
{
  int lines = devrow - 6;

  if (news_context == 1) cur_up_grp(lines,1);
  else {
    set_level(2);
    cur_up_itm(curr_g,lines,1);
    }
  return(0);
}

int
do_abort()
{
    closefiles_opt(0,0);
    exit(1);
    /*NOTREACHED*/
    return 0;
}

static char *dat()
{
  static char datum[32];
  time_t cur_time;
  char *p;

  time(&cur_time);
  p = ctime(&cur_time);
  strncpy(datum, p, 19);
  return(datum);
}

void
log_to_usage_file(flag,group)
  int flag;				/*  0 - initialize data areas */
					/*  1 - dump data to logging file */
					/* -1 - read article from group */
					/* -2 - post article to group */
  int group;				/* group number for read/post */
{                                       /* logging declarations */
  FILE *fpl;
  int g, i, retry_cnt;
  long dif_time;
  time_t end_time;
  static time_t start_time;
  static int max_index, read_count, post_count;
  static int *group_nums, *read_groups, *post_groups;
  char *trans;
  typedef struct _ItemList
  {
    unsigned short buffer_length;
    unsigned short item_code;
    void *buffer_address;
    unsigned short *return_length_address;
  } ItemList;
  char account[9];
  unsigned short accountlen = 0;
  ItemList
    getuai_itmlist[] =
      { {(sizeof(account)-1), UAI$_ACCOUNT, account, &accountlen},
        {0,0,0,0} };

  if (flag == 0) {
    time(&start_time);
    max_index = 0;
    read_count = 0;
    post_count = 0;
    group_nums  = (int *) news_malloc(100 * sizeof(int));
    read_groups = (int *) news_malloc(100 * sizeof(int));
    post_groups = (int *) news_malloc(100 * sizeof(int));
    return;
    }
  if (group) group = ga[group]->grp_num;
  if (flag < 0) {
    for (g = 0; g < max_index; g++)
      if (group_nums[g] == group) break;
    if (g == max_index) {
      if (max_index == 100) g = -1;
      else {
        max_index++;
        group_nums[g] = group;
        read_groups[g] = 0;
        post_groups[g] = 0;
        }
      }

    if (flag == -1) {
      read_count++;
      if (g >= 0) read_groups[g]++;
      return;
      }
    else if (flag == -2) {
      post_count++;
      if (g >= 0) post_groups[g]++;
      return;
      }
    }
  /*  if (!no_priv()) return;  */
  if ( (read_count + post_count) == 0 ) return;
                   /*    Translate logical name. */
  if (!(trans = news_getenv("NEWS_USAGE_LOG",1))) return;
  sysprv();
  retry_cnt = 3;
  do {
    if (!(fpl = fopen(trans,"a","shr=get","deq=50"))) sleep(1); else break;
    } while (--retry_cnt >= 0);
  if (fpl) {
    time(&end_time);
    dif_time = (start_time > end_time) ? 0L : end_time - start_time;
    if (news_getenv("NEWS_USAGE_ANONYMOUS",1))
    {
	sys$getuai(0,0,c$dsc(usr_username),getuai_itmlist,0,0,0,0);
	account[accountlen] = '\0';
      	fprintf(fpl,"%s: Account %s - %ld minutes - %d articles read - %d posted\n",
      	  dat(), account, (dif_time + 30) / 60, read_count, post_count);
    }
    else
      fprintf(fpl,"%s: User %s - %ld minutes - %d articles read - %d posted\n",
      dat(), usr_username, (dif_time + 30) / 60, read_count, post_count);
    for (i = 0; i < max_index; i++) {
      g = ga_locate(group_nums[i]);
      if (!g) continue;
      fprintf(fpl,"\t%s %d %d\n",ga[g]->grp_name,read_groups[i],post_groups[i]);
      }
    fclose(fpl);
    }
  nosysprv();
}


/*
 * mem_fail - called by the memory allocation routines when an allocation
 * request fails.  Prompt the user to save NewsRC, clear mem, and unwind
 * to main loop via news_condition_handler.
 */

void *mem_fail(bytesize)
  size_t bytesize;
{
  char reply[16];
  unsigned short int replylen, trm;
  $DESCRIPTOR(replydsc,reply);

#if !SIMPLE_MALLOC
  if (mem_reserve < MEM_RESERVE_SIZE) {
    if (mem_reserve) {
      mem_reserve = 0;
      err_line("Fatal memory shortage - exiting");
      }
    else printf("Fatal memory shortage - exiting\n");
    exit(LIB$_INSVIRMEM);
    }

  /* decrease the temp reserve so err_line(), get_input_dflt(), and mem_reset()
     have space to work.  Hang on to enough to output a fatal error message
     via err_line() if mem_reset fails  */
  mem_reserve = min(1024,MEM_RESERVE_SIZE/2);
#endif
  c$free_tmp();

  err_line("Out of memory - clearing buffered data");

  *reply = 'X';
  while ((*reply != 'Y') && (*reply != 'N')) {
    if (get_input_dflt(&replydsc,c$dsc("Update NewsRC (Y/N)? "),&replylen,
                            c$dsc("Y"),&trm) == RMS$_EOF) *reply = 'N';
    *reply = toupper(*reply);
    }

  if (*reply == 'N') {
    *reply = 'X';
    err_line("'N' will NOT save any REGISTER, READ, MARK, SKIP commands from this session");
    while ((*reply != 'Y') && (*reply != 'N')) {
      if (get_input_dflt(&replydsc,c$dsc("Update NewsRC (Y/N)? "),&replylen,
                              c$dsc("Y"),&trm) == RMS$_EOF) *reply = 'N';
      *reply = toupper(*reply);
      }
    }

  clear_err_line();
  mem_reset(cur_dir_type,(toupper(*reply) == 'Y'));

  mem_reserve = MEM_RESERVE_SIZE;

  /* unwind to the main loop - the handler established there
     must expect this condition value */
  lib$signal(NEWS$_UNWIND,0);

  /* the condition handler set up to unwind, now do it */
  return (void *) 0;

}  /*  end of mem_fail()  */


/*
 *  putmsg_action_routine
 *
 *  Called as the action routine from $PUTMSG.
 *
 *  Appends error message (possibly multi-line) to the global
 *  array 'err_msg' (of length 'err_msg_l'); lines are terminated by '\0'.
 *  Number of lines in err_msg is updated in err_msg_lines;
 */

int putmsg_action_routine(str_dsc, actprm)
  struct dsc$descriptor_s *str_dsc;
  int actprm;
{
  int j,len,from,upto,offs;
  char c;

  len = str_dsc->dsc$w_length;
  for (offs = from = 0; from < len; offs = 4) {
    upto = min(from+(80-4)-offs, len-1);
    if (upto < len-1) {                      /* try to wrap at word boundary */
      while ((upto > from+20) && (str_dsc->dsc$a_pointer[upto] != ' ')) upto--;
      }
    for (j=0; j<offs; j++)
      if (err_msg_l < (sizeof err_msg)-1) err_msg[err_msg_l++] = ' ';
    while (from <= upto) {
      c = str_dsc->dsc$a_pointer[from++];
      if      (c == '\n') break;  /* stop at newline */
      else if (c == '\r') ;      /* ignore CR */
      else if (err_msg_l < (sizeof err_msg)-1) err_msg[err_msg_l++] = c;
      }
    err_msg[err_msg_l++] = '\0'; err_msg_lines++;
    }
  return 0;     /* do not allow $PUTMSG to write message itself! */
}


/*
 *  news_condition_handler
 *
 *  Condition handler
 *  Also handles NEWS$_UNWIND signal (e.g. from mem_fail() )
 */


static unsigned long int mem_fail_ret()
{
  establish_handler(news_condition_handler);
  return (unsigned long int) mem_fail(0);
}


int news_condition_handler(sigargs_v,mechargs_v)
  void  *sigargs_v;
  void  *mechargs_v;
{
  struct chf$signal_array *sigargs = (struct chf$signal_array *)sigargs_v;
  struct chf$mech_array   *mechargs = (struct chf$mech_array *)mechargs_v;
  static volatile int this_handler_active = 0;
  struct dsc$descriptor_const_s dsc;
  int cond,nargs,last_cond,facility,severity,j,line_no,sts;
  int mailer_syntax_err;  /* mailer signalled a user error (e.g. bad username)*/
  int cli_syntax_err;     /* CLI signalled a command syntax error */
  int will_abort;         /* abort program, instead of trying to continue */
  unsigned short message_options;
  int new_sa[16];         /* copy of the signal array to be passed to $PUTMSG */
  int MAIL$_TEXT       = 0x007e1130;
  int MAIL$_NOSUCHUSR  = 0x007e802a;
  int MAIL$_USERSPEC   = 0x007e8062;
  int MAIL$_LOGLINK    = 0x007e803a;
#define CLI$_FACILITY	3
#define MAIL$_FACILITY	126

  cond  = sigargs->chf$l_sig_name;
  nargs = sigargs->chf$l_sig_args;
  last_cond = nargs<3 ? 0 : ((int *)sigargs)[nargs-2];
#if GNUC_HANDLER_HACK
  if (cond == C$_LONGJMP)
    return(this_handler_active=0,vaxc$unwind_longjmp(sigargs,mechargs));
#endif
  if (cond == SS$_UNWIND || last_cond == SS$_UNWIND)
    return(this_handler_active=0,SS$_CONTINUE);           /* return any value */
/*
 * the last_cond fudge:
 *   The callable mail interface to VMSMAIL has a bug in its condition
 * handler. During handler UNWIND every active condition handler receives
 * a SS$_UNWIND signal to which it should perform internal cleanup and
 * return.  The VMSMAIL's condition handler instead does not recognise
 * SS$_UNWIND, but wraps it into a wrapper signal and resignals it.
 * To prevent a loop this handler recognizes this bug and checks whether
 * the wrapped (last) signal is SS$_UNWIND and ignores it. 
 */
  if (cond == SS$_ASTFLT)return(this_handler_active=0,SS$_RESIGNAL);/*VAXC signals*/
  if (cond == SS$_DEBUG) return(this_handler_active=0,SS$_RESIGNAL);
  if (this_handler_active && cond != NEWS$_UNWIND) return(SS$_RESIGNAL);
  this_handler_active = 1;
  facility = $VMS_STATUS_FAC_NO(cond);
  severity = $VMS_STATUS_SEVERITY(cond);

    /* some day CLI errors should be categorized to user and program errors */
  cli_syntax_err    =  (facility == CLI$_FACILITY);
  mailer_syntax_err = ((facility == MAIL$_FACILITY) &&
                       (cond == MAIL$_TEXT ||
                        cond == MAIL$_NOSUCHUSR ||
                        cond == MAIL$_USERSPEC ||
                        cond == MAIL$_LOGLINK));

  /* try to put an end to an infinite loop through the condition handler
     when UNWIND or CONTINUE are repeatedly causing another error */
  if (!(severity&1) && !cli_syntax_err && !mailer_syntax_err) {
    if (++signalled_error_count > 42 /*any reasonably high number*/ ) {
      /* reclassify on_error: make it less tolerant to errors */
      if (on_error == ON_ERROR_CONTINUE  ||  on_error == ON_ERROR_UNWIND) 
        { on_error = ON_ERROR_ABORT; signalled_error_count = 1; }
      else if (on_error == ON_ERROR_ABORT) abort();
      }
    }

  err_msg_l = err_msg_lines = 0;
  will_abort = 0;
  if (0  /* severity == STS$K_SEVERE */) will_abort = 1;
  else if (cli_syntax_err || mailer_syntax_err) /* do not abort */;
  else if (!(severity&1) || cond == NEWS$_UNWIND)
    if (on_error == ON_ERROR_ABORT) will_abort = 1;

  if (will_abort || cond == NEWS$_UNWIND) ; /* do not want a message */
  else {
    /* obtain a full message text */
    message_options = 0;  /* use process default */
#if STRIP_ERR_LINES
    if (cli_syntax_err) message_options = 0x01; /*message text only*/
#endif
    new_sa[0] = (message_options<<16) | min(16-1,nargs-2);
    for (j = 1; j < 16; j++)
      if (j <= nargs-2) new_sa[j] = ((int *)sigargs)[j]; else new_sa[j] = 0;
    sys$putmsg(new_sa,&putmsg_action_routine,0,0);
    dsc.dsc$b_dtype = DSC$K_DTYPE_T;  dsc.dsc$b_class = DSC$K_CLASS_S;
    }
  if (err_msg_lines) {
    /* write the full message text first */
    for (j=0, line_no=1; j<err_msg_l; j+=(dsc.dsc$w_length+1), line_no++) {
      dsc.dsc$a_pointer = (const char *) &err_msg[j];  push_brdmsg_raw(&err_msg[j]);
      dsc.dsc$w_length = strlen(dsc.dsc$a_pointer);
      if (!smg_active) lib$put_output(&dsc);
      else if (line_no <= 1) {
        /* avoid using dynamic memory allocation (c$* routines) */
        int row = 3, col = 1;
        int flags = SMG$M_ERASE_LINE, rendition = SMG$M_REVERSE;
        sts = smg$put_chars(&trailer_vd,&dsc,&row,&col,&flags,&rendition,0,0);
        if (! (sts & 1)) lib$put_output(&dsc);
        }
      }
    if (smg_active) {    /* flush SMG output */
      int j, smg_batching_depth = 0;
      /* reduce batching depth to zero */
      while ((sts=smg$end_pasteboard_update(&pid)) == SMG$_BATSTIPRO)
        smg_batching_depth++;
      if (sts == SS$_NORMAL) smg_batching_depth++;
      if (sts&1) {
        smg$flush_buffer(&pid);  /* ignore returned status */
        /* restore batching depth */
        for (j = 1; j <= smg_batching_depth; j++) {
          sts = smg$begin_pasteboard_update(&pid);
          if (! (sts&1) ) break;
          }
        }
      }
    }
  if (will_abort) {
    problems_encountered = 1;
    /* change severity to SEVERE and abort with traceback */
    cond = (cond & (~7)) | STS$K_SEVERE;
    sigargs->chf$l_sig_name = cond;
    return(this_handler_active=0,SS$_RESIGNAL);
    }
  else if (cli_syntax_err)
    return(this_handler_active=0,SS$_CONTINUE);/* no panic on syntactic errors*/
  else if (cond == LIB$_INSVIRMEM ||  /* try to free up some memory */
           cond == OTS$_INSVIRMEM ||  /* and continue */
           cond == SHR$_INSVIRMEM ||
           cond == STR$_INSVIRMEM)
    severity = $VMS_STATUS_SEVERITY(cond = mem_fail_ret());

  if (!(severity&1) || cond == NEWS$_UNWIND) {
    if (!mailer_syntax_err) problems_encountered = 1;
    if (on_error == ON_ERROR_CONTINUE && cond != NEWS$_UNWIND) {
      cond = (cond & (~7)) | STS$K_WARNING;
      return(this_handler_active=0,SS$_CONTINUE);
      }
    else {
   /* return 0 to wherever in main() we pick up - probably doesn't matter,
      but FALSE more likely (?) not to be taken as something having been
      completed successfully  */
#ifdef __ALPHA
      if (mechargs->chf$q_mch_depth <= 0) sys$unwind(0,0);
#else
      if (mechargs->chf$l_mch_depth <= 0) sys$unwind(0,0);
#endif
      else {
#ifdef __ALPHA
        mechargs->chf$q_mch_savr0 = 0;
#else
        mechargs->chf$l_mch_savr0 = 0;
#endif
        /* unwind back into main(), rather than exit */
#ifdef __ALPHA
        sys$unwind(&(mechargs->chf$q_mch_depth),0);
#else
        sys$unwind(&(mechargs->chf$l_mch_depth),0);
#endif
        }
      /* status is ignored after a call to $unwind */
      return(this_handler_active=0,SS$_CONTINUE);
      }
    }
  else if (severity == STS$K_SUCCESS ||
           severity == STS$K_INFO)
    return(this_handler_active=0,SS$_CONTINUE);

  return(this_handler_active=0,SS$_RESIGNAL);
}
