/*
**++
**  FACILITY:
**      NEWSUTIL1
**
**  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    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.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.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  12-Apr-1994  mark.martinec@ijs.si
**	 - do_new_item() receives an extra argument to disable some
**	   cases of signalling errors
**	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  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   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  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 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"
**	V6.1b10  9-Sep-1996	Mark Martinec   mark.martinec@ijs.si
**	  - new routines: ga_find_group_by_name (uses binary search),
**	    and test_groups_sorted()
**	  - hand optimize the hot CPU spot in the client:
**	      sprintf(err_oline,"   %-5d %c %-*.*s           %5d", ...
**	V6.1b10 12-Sep-1996	Mark Martinec   mark.martinec@ijs.si
**	  - split module NEWSUTILITY.C into NEWSUTIL[1,2,3].C
**	    because of its size (4800 lines) which made testing tedious.
**	    Also split revision history comments.
**	    NEWSUTIL1 - handles mostly application code, file i/o,
**	                groups and items-related data structures, NNTP, ...
**	    NEWSUTIL2 - more general-purpose routines, formatting,
**	                SMG, error handling, ...
**	    NEWSUTIL3 - support routines needed by NEWS and NNTP_SERVER
**	                (like gen_id() and its support routines)
**	V6.1b10 09-DEC-1996	Mark Pizzolato	mark@infocomm.com
**	  - minor declaration cleanup to get warningless compile under 
**	    DEC C V5.3 on VAX
**	  - moved definition of global nntp_currgroup to NEWSVARIABLES.H
**	    this was referenced elsewhere in NEWSREMCLIENT.C.
**	V6.1b11	21-Feb-1997     Mark Martinec   mark.martinec@ijs.si
**	  - cleanup semantics: change parameter name server_name
**	    to file_opened and its type to int
**	  - pass nntp_node as a server name when calling
**	    get_server_file and get_server_header (fixes EXTRACT/TITLE bug)
**	  - clean-up syntactic problems when compiling with NNTP_CLIENT=1
**	V6.1b11 14-Apr-1997	Mark Martinec   mark.martinec@ijs.si
**	  - get_itm_by_ident() now returns *rg properly even as a nntp client
**	    (READ/REF was failing with ACCVIO)
**	  - replace several calls to strcpy with safer strnncpy
**	  - replace pattern strcpy(news_malloc(strlen(x)+1),x) with news_strdup
**	  - replace pattern strcat(err_oline,x) with err_oline_cat(x)
**	  - change %s to %.<n>s in sprintf to guard against overwriting buffer
**          (with somewhat arbitrary limits) - some of them still left unchecked
**	  - use new variable displ_subjlen to dynamically control the
**	    item title and newsgroup name size on the display, depending
**	    on the current screen width
**	  - ga_search_name(): perform a caseless search, since we must
**	    store newsgroup names as received from NNTP server exactly
**	    (see RFC977, chapter: 3.2.1. GROUP) but match them
**	    regardless of case.
**--
**/

#ifdef vaxc
#module NEWSUTIL1 "V6.2"
#endif

#define _NEWSUTIL1_C
#define module_name "NEWSUTIL1"

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

#if NAKED_INCLUDES
#include uaidef
#else
#include <uaidef.h>
#endif

#ifdef __GNUC__	/* old gcc header has bad prototype */
extern int sleep __ARGS((unsigned));
#endif

extern char dir_sincestr[];
extern int profile_flags;


/*
 * test_groups_sorted - tests the ga array and write warnings
 *   if groups are not sorted by their names. A debug function.
 */
int test_groups_sorted()
{ int g,cmp;
  for (g = 2; g <= ga_size; ++g) {
    cmp = strcmp(ga[g]->grp_name, ga[g-1]->grp_name);
    if     (cmp==0){ printf("Duplicate name: %s\n",ga[g]->grp_name); return 0; }
    else if (cmp<0){ printf("Not sorted: %s\n",    ga[g]->grp_name); return 0; }
    }
  return 1;
}

/*
 * ga_find_group_by_name - locate group in the ga array by its name;
 *   Assumes the groups are alphabetically sorted by name in the ga array.
 *   Returns index of the group in the ga array if found, otherwise 0.
 *
 *   Optionally (if indx != NULL) returns the same index in the second
 *   parameter if the group was found, or returns the index of the location
 *   where a group with this name should have been located (= where it
 *   should be inserted).
 */
int ga_find_group_by_name(grpname,indx)
  const char *grpname;
  int *indx;
{
  int m,lb,ub;
  int cmp,found;

  news_assert(!reorder_groups);  /* groups in ga must be sorted by name */
  lb = m = 1; ub = ga_size; found = 0;
  while (lb <= ub) {  /* binary search */
    m = (lb+ub)/2;
    cmp = strcmp(ga[m]->grp_name,grpname);
    if (!cmp) { found++; break; }
    else { if (cmp>0) ub = m-1; else lb = m+1; }
    }
  if (indx) *indx = found?m:lb;
  return(found?m:0);
}

/*
 *  ga_exact_name
 *
 *  locate a newsgroup by name - exact match
 */
int ga_exact_name(s)
  const char *s;
{
  static int prev_gi = 0;

  /* 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 */
    prev_gi = ga_find_group_by_name(s,0);
    }
  else {    /* else use old code. */
    int i;
   /* the old code used to do a trick with single byte compares
      which no longer works since ga[i]->grp_name is dynamically
      allocated and may be shorter than GRPNAMELEN */
    prev_gi = 0;
    for (i = 1; i <= ga_size; ++i)
      if (!strcmp(s, ga[i]->grp_name)) { prev_gi = i; break; }
    }
  return prev_gi;
}

/*
 *  ga_search_name
 *
 *  locate a newsgroup by name - allowing partial match
 *
 *  Search should be case insensitive, since we must store newsgroup names
 *  as received from NNTP server exactly (see RFC977, chapter: 3.2.1. GROUP)
 *  but match them regardless of case.
 *
 *  The performance is not critical since this is mostly used interactively,
 *  one search per command.
 */
int ga_search_name(s)
  const char *s;
{
  int j = 0, i, s_l = strlen(s);
  char c = _tolower(*s);

  for (i = 1; i <= ga_size; ++i) {
    if (_tolower(*ga[i]->grp_name) != c) continue;
    if (!news_strncasecmp(s,ga[i]->grp_name,s_l)) {
      j = i;
      if (!news_strncasecmp(s,ga[i]->grp_name,0)) return(i);
      }
    }
  return(j);
}

/*
 *  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;
  GRPF savegrp;
  int new_mem = 0;
  char s[GRPNAMELEN];

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

  util_cvrt(s,g);
  grprab.rab$l_kbf = s;
  grprab.rab$b_ksz = GRPNAMELEN_F;
  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)) {
      strcpy(err_oline,"Newsgroup: "); err_oline_cat(s);
      if (!acpt) err_oline_cat(" (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;
    strncpy(newsgrp.grp_name,s,GRPNAMELEN_F);
    newsgrp.grp_topnum = 0;
    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_flags = 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;

  *retg = 0;
  locg = news_strdup(g,-1);
  for (cp1=locg;  (cp2=strchr(cp1,',')) != 0;  cp1=cp2) {
    *cp2++ = '\0';
    if ((*retg = do_newg(cp1,prompt,1)) != 0) *++retg = 0;
    }
  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;
  *retg = 0;
  locg = news_strdup(g,-1);
  for (cp1=locg;  (cp2=strchr(cp1,',')) != 0;  cp1=cp2) {
    *cp2++ = '\0';
    if ((*retg = do_newg(cp1,prompt,0)) != 0) *++retg = 0;
    }
  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;

  *retg = 0;
  locg = news_strdup(g,-1);
  for (cp1=locg;  (cp2=strchr(cp1,',')) != 0;  cp1=cp2) {
    *cp2++ = '\0';
    if ((*retg = do_newg(cp1,prompt,1)) != 0) *++retg = 0;
    }
  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,file_opened,protected,flk)
  const char *mode;
  int *file_opened;     /* returned file descriptor is valid and file is open */
  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 (file_opened) *file_opened = 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 && file_opened) *file_opened = 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 (file_opened) *file_opened = 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,file_opened,protected,flk)
  const ITM *itm;
  const char *mode, *grp_name;
  int *file_opened;     /* returned file descriptor is valid and file is open */
  int protected, *flk;
{
  sprintf(itm_fname,Itm_template,util_dir(grp_name),itm->itm_num);
  return(do_openi(mode,file_opened,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;
  const char *id, *subj, *fromstr, *fnam;
  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);
  strnncpy(xrefline,xrefline_s, "Xref: ",-1, &xrefline_l);
  strnncat(xrefline,xrefline_s,xrefline_l, news_node,-1, &xrefline_l);

  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) {
        { int ll;
          strnncpy(mod,sizeof(mod), usr_username,-1, &ll);
          strnncat(mod,sizeof(mod),ll, "@",1, &ll);
          strnncat(mod,sizeof(mod),ll, Node_address,-1, 0);
        }
        forward_posting = 0;
        if (!strcmp(mod,moderator_address(util_grp_name_str(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(util_grp_name_str(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(util_grp_name_str(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]," %.200s:%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 - %.70s/0x%x)",
		strerror(errno,vaxc$errno),rms$errno);
          status = 0x90000000U;
          *itm_fname = '\0';
          continue;
          }
        while (rms_get(ibuf,sizeof(ibuf),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 - %.70s/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 = new_group(&newsgrp);
  if (reorder_groups) i = ga_size + 1;
  else ga_find_group_by_name(util_grp_name_str(newsgrp.grp_name), &i);
  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();
}

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,displ_subjlen,displ_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+displ_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,file_opened)
  int g,i;
  const char *mode;
  int *file_opened;     /* returned file descriptor is valid and file is open */
{
  FILE *tmp = 0;
  char *nntp_response=NULL;
  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,file_opened));
    else return(0);
    }

  if (nntp_client) {
    nntp_respval = 211;
    if (strcmp(ga[g]->grp_name,nntp_currgroup)) {
      strcpy(err_oline,"GROUP "); err_oline_cat(ga[g]->grp_name);
      nntp_respval = nntp_one_call(nntp_node,nntp_proto,err_oline,&nntp_response);
      if (nntp_respval == 211)
        strnncpy(nntp_currgroup,nntp_currgroup_m, ga[g]->grp_name,-1, 0);
      }
    if (nntp_respval == 211)
      tmp = getno_server(ga[g]->grp_ia[i].itm_num,ga[g]->grp_num,
                         nntp_node,file_opened,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,file_opened,
			(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, nntp_node, file_opened,
		   ((ga[g]->grp_flags & NEWS_M_NNTPCACHE) ? itm_fname : 0),
		   nntp_proto,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+1)/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)) {
    for (header_line += 5;  isspace(*header_line);  header_line++) {}
    if ((cp = chop_str_plus(header_line,':')) != 0) {
      if (s_newsgroup) news_free(s_newsgroup);
      s_newsgroup = news_strdup(header_line,-1);
      lower_case(s_newsgroup);
      sscanf(cp,"%d",&s_inum);
      }
    return 0;
    }
  if (strncmp(header_line,"Newsgroups:",11)) return 0;
  chop_str(header_line,'\n');
  for (cp = &header_line[11];  isspace(*cp);  cp++) {};
  s_newsgroup = news_strdup(cp,-1);
  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)) {
    for (cp = &header_line[6];  isspace(*cp);  cp++) {}
    util_fromcpy(newsitm.itm_from,cp);
    }
  else if (!news_strncasecmp(header_line,"Subject:",8)) {
    for (cp = &header_line[8];  isspace(*cp);  cp++) {}
    util_subjcpy(newsitm.itm_title,cp);
    }
  if (!news_strncasecmp(header_line,"Message-ID:",11)) {
    for (cp = &header_line[11];  isspace(*cp);  cp++) {}
    util_idcpy(newsitm.itm_id,cp);
    }
  else if (!news_strncasecmp(header_line,"Date:",5)) {
    int tmp;
    for (cp = &header_line[6];  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=NULL;
  int count, first, last, g, i;
  int status;

  *rg = 0;  *ri = 0;
#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;
  strcpy(err_oline,"HEAD "); err_oline_cat(id);
  if (!nntp_get_info(nntp_node,nntp_proto,err_oline,221,sinum,snewsgroup)) {
    if (def_g) s_newsgroup = news_strdup(ga[def_g]->grp_name, -1);
    else return(0);
    }
  for (cp1=s_newsgroup; cp1; cp1=cp2) {
    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;
      }
    *rg = g;
    if (find_itm_by_id(g, id, (unsigned int *)ri)) return(1);
    strcpy(err_oline,"GROUP "); err_oline_cat(cp1);
    if (nntp_one_call(nntp_node,nntp_proto,err_oline,&nntp_response) != 211) continue;
    strnncpy(nntp_currgroup,nntp_currgroup_m, cp1,-1, 0);
    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;
      for (tmp = ga[g]->grp_idhead;  tmp;  tmp=tmp->f_link) {
        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;
          }
        }
      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);
    }
  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=NULL;
  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
    strcpy(err_oline,"GROUP "); err_oline_cat(ga[grp]->grp_name);
    if (nntp_one_call(nntp_node,nntp_proto,err_oline,&nntp_response) != 211)
      return 0;
    strnncpy(nntp_currgroup,nntp_currgroup_m, ga[grp]->grp_name,-1, 0);
    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,file_opened)
  int g,i;
  const char *mode;
  int *file_opened;     /* returned file descriptor is valid and file is open */
{
  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,file_opened,
			(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, nntp_node,
                          file_opened, nntp_proto,
                          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);
}

/*
 *  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);
}

/*
 *  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));
    }

  for (j = sgap->grp_count + 1;  j > i;  j--) {
    sgap->grp_ia[j] = sgap->grp_ia[j-1];
    }
  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));
    }
  for (j = sgap->grp_count + 1;  j > i;  j--) {
    sgap->grp_ia[j] = sgap->grp_ia[j-1];
    }
  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;
  int f_itm, l_itm, i, j, 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) {
#if !NNTP_CLIENT
    exit(NEWS$_NONNTP);
#else
    char subject[256];
    char *bufptr=NULL, *cp1, *cp2, *cp3;
    int count, first, last, indx, xhdr_call = 0;

    *nntp_currgroup = '\0';
    strcpy(err_oline,"GROUP "); err_oline_cat(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+displ_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);
                for (; cp1; cp1=cp2) {
	  	  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);
		    }
                  }
                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;
                }
              }
	    }
          }
        }
      gap->grp_topnum = !gap->grp_count ? last
                                        : gap->grp_ia[gap->grp_count].itm_num;
      }
#endif
    }
  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 (gap->grp_reg_text) {
    for (itm_list = gap->grp_reg_text;  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) {
        for (; itm_list; itm_list=cp) {
	  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;
	    }
          }
        }
      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;
          for (j = i+1; j <= gap->grp_count; j++) l_ia[j-1] = l_ia[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;
      strnncpy(title,sizeof(title), tmp[j].itm_title,-1, 0);
      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;
        strnncpy(tmp_title,sizeof(tmp_title), gap->grp_ia[k].itm_title,-1, 0);
        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;
{
#if NNTP_CLIENT
  char *bufptr=NULL;
  int count, first, last;
#endif

  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';
  strcpy(err_oline,"GROUP "); err_oline_cat(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;
{
#if NNTP_CLIENT
  char *bufptr=NULL;
  int count, first, last;
#endif

  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';
  strcpy(err_oline,"GROUP "); err_oline_cat(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);
}

#if NNTP_CLIENT
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=NULL;
  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';
  strcpy(err_oline,"GROUP "); err_oline_cat(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 %u-%u",
	  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 %u-%u",
	  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);
}
#endif /* NNTP_CLIENT */

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);
#if NNTP_CLIENT
  ds_postmark(curr_g);
#endif
  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);
}
#endif /* NNTP_CLIENT */

int map_ids(g)
  unsigned int g;
{
  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_CLIENT && 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)) {
    char *nntp_line;
    strcpy(err_oline,"GROUP "); err_oline_cat(ga[g]->grp_name);
    if (nntp_one_call(nntp_node,nntp_proto,err_oline,&nntp_line) != 211)
      return(0);
    strnncpy(nntp_currgroup,nntp_currgroup_m, ga[g]->grp_name,-1, 0);
    }
  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_CLIENT && NNTP_USE_XHDR */
  return(1);
}

/*
 *  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();
}

/*
 *  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));
        }

   /**** hard-code the following heavy consumer of CPU; mark.martinec@nsc.ijs.si
    * if (nntp_client && ga[g]->grp_firstnum < 1)
    *     sprintf(err_oline,"   %-5d %c %-*.*s           %5d",sg,
    *         ((ga[g]->grp_flags & NEWS_M_NNTPSRV)? '_' : ' '),displ_subjlen,displ_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)? '_' : ' '),displ_subjlen,displ_subjlen,
    *         ga[g]->grp_name,ga[g]->grp_count,ga[g]->grp_unread);
    */
      { int k,l,n,m,f, j=0;
        char *cp;
        err_oline[j++] = ' ';  err_oline[j++] = ' ';  err_oline[j++] = ' ';
        n = sg;
        for (k=11; ; ) {      /*  "%-5d"  */
          m = n/10;  k--;  err_oline[j+k] = (n-10*m) + '0';
          if ((n=m) == 0) break;
          }
        f = j+k;
        for (l=   11-k;  l>0; l--) err_oline[j++] = err_oline[f++];
        for (l=5-(11-k); l>0; l--) err_oline[j++] = ' ';
        err_oline[j++] = ' ';
        err_oline[j++] = (ga[g]->grp_flags & NEWS_M_NNTPSRV) ? '_' : ' ';
        err_oline[j++] = ' ';
        cp = ga[g]->grp_name;   /*  "%-*.*s"  displ_subjlen,displ_subjlen  */
        for (k=1; *cp && k<=displ_subjlen; k++) err_oline[j++] = *cp++;
        for (   ;        k<=displ_subjlen; k++) err_oline[j++] = ' ';
        err_oline[j++] = ' ';  err_oline[j++] = ' ';
        if (nntp_client && ga[g]->grp_firstnum < 1)
          for (k=1; k<=5; k++) err_oline[j++] = ' ';
        else {
          n = ga[g]->grp_count;
          for (k=11; ; ) {      /*  "%5d"  */
            m = n/10;  k--;  err_oline[j+k] = (n-10*m) + '0';
            if ((n=m) == 0) break;
            }
          f = j+k;
          for (l=5-(11-k); l>0; l--) err_oline[j++] = ' ';
          for (l=   11-k;  l>0; l--) err_oline[j++] = err_oline[f++];
          }
        for (k=1; k<=4; k++) err_oline[j++] = ' ';
        n = ga[g]->grp_unread;
        for (k=11; ; ) {        /*  "%5d"  */
          m = n/10;  k--;  err_oline[j+k] = (n-10*m) + '0';
          if ((n=m) == 0) break;
          }
        f = j+k;
        for (l=5-(11-k); l>0; l--) err_oline[j++] = ' ';
        for (l=   11-k;  l>0; l--) err_oline[j++] = err_oline[f++];
        err_oline[j] = '\0';
      }
      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(displ_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 %.55s [SINCE=%s, %d Newsgroups]",class_name(),dir_sincestr,sg);
  else if (cur_dir_type == DIR_NEW)
    sprintf(dir_type,"NEWSGROUPS %.55s [NEW, %d Newsgroups]",class_name(),sg);
  else if (cur_dir_type == DIR_REGISTER)
    sprintf(dir_type,"NEWSGROUPS %.55s [REGISTERED, %d Newsgroups]",class_name(),sg);
  else if (cur_dir_type == DIR_UNREGISTER)
    sprintf(dir_type,"NEWSGROUPS %.55s [UNREGISTERED, %d Newsgroups]",class_name(),sg);
  else
    sprintf(dir_type,"NEWSGROUPS %.55s [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) {
    err_oline_cat("Reg[002]:[101]");
    sprintf(itemhold,"%u",gap->grp_reg); err_oline_cat(itemhold);
    }

  if (gap->grp_flags & NEWS_M_RESTRICT_SET) {
    if (*err_oline) err_oline_cat(",[121]");
    err_oline_cat("Restrict[005]");
    }

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

  if (nntp_client) {
    if (*err_oline) err_oline_cat(",[121]");
    err_oline_cat("Server[005]:[101]");
    err_oline_cat(nntp_node);
    }
  else if (ga[g]->grp_flags & NEWS_M_NNTPSRV) {
    if (*err_oline) err_oline_cat(",[121]");
    err_oline_cat("Server[005]:[101]");
    if (gap->grp_srvnode) err_oline_cat(gap->grp_srvnode);
    if (gap->grp_srvproto == DECNET) err_oline_cat("::");
    if (gap->grp_flags & NEWS_M_NNTPCACHE) {
      err_oline_cat(",[121](cache[004]:[101]");
      sprintf(itemhold,"%d)",gap->grp_srvcache); err_oline_cat(itemhold);
      }
    }
}

void newsgroup_header()
{
  char tmp[512];
  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,"%.100s[005]:[171] %d Items[164](#%d - #%d)[171]",
               gap->grp_name, gap->grp_count,
               iap[1].itm_num, iap[gap->grp_count].itm_num);
  else sprintf(tmp,"%.100s[005]:[171] %d Items[174]",
               gap->grp_name, gap->grp_count);
  strnncat(tmp,sizeof(tmp),-1, err_oline,-1, 0);
  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 && *(gap->grp_topic)) {
    sprintf(tmp,"        -<%.200s>-", 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 && *(gap->grp_notice)) {
    sprintf(tmp,"         [Notice: %.200s]",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;
}

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);
}

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();
  for (retry_cnt=3; retry_cnt>=0; retry_cnt--) {
    if (!(fpl = fopen(trans,"a","shr=get","deq=50"))) sleep(1); else break;
    }
  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();
}
