/*
**++
**  FACILITY:
**      NEWSSKIM
**
**  ABSTRACT:
**      SKIM/ITEMS
**         Delete all items which are unreadable, or have been on the
**         system for longer than EXP_TIME days, or have no message identifier.
**      SKIM/NEWSGROUPS:
**         Delete all newsgroups which have had no new items entered
**         in the last GRP_TIME days, and have no current items.
**      SKIM/FILES:
**         Delete all ITM files which are backversions or are not
**         referenced by the item file.
**
**  AUTHOR:
**      Geoff Huston
**
**  COPYRIGHT:
**      Copyright © 1988,1989,1990,1991
**
**  VERSION HISTORY:
**	V6.1b8  22-Dec-1993	mark.martinec@ijs.si
**	   - convert calls to RMS to use new sys_* macros for error handling 
**	V6.1b8   3-Mar-1994	mark.martinec@ijs.si
**	   - check status on delete() and fclose()
**	V6.1b9  25-May-1994  Charles Bailey  bailey@genetics.upenn.edu
**	   - add flk param to do_oitem calls and _ck_*_info macros so
**	     file error doesn't cause entire skim pass to abort
**	V6.1b9   2-Jun-1994	bailey@genetics.upenn.edu
**	   - once a newsgroup is deleted, don't try to fix item counts in
**	     (now nonexistent) group index record
**	V6.1b9  23-Jul-1994	Charles Bailey  bailey@genetics.upenn.edu
**	   - fixed bug in doskim() which caused infinite loop if newsgroups
**	     pattern specified in Skim command matched all groups
**	   - fixed bug in grp_skim() which caused groups to be processed twice
**	     when skimming a subset of newsgroups
**	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)
**	V6.1b10	28-Feb-1996     Mark Pizzolato  mark@infocomm.com
**	  - revised SKIM/ITEMS to delete items in reverse numeric order
**	    which will always be quicker on any ODS II file system.
**	V6.1b10	25-Oct-1996     Mark Pizzolato  mark@infocomm.com
**	  - fixed previous reverse order delete to avoid memory leak that
**	    it introduced.
**	V6.1b10	25-Oct-1996     Mark Pizzolato  mark@infocomm.com
**	  - Changed SKIM/ITEMS and SKIM/NEWSGROUPS to process groups in 
**	    lexical order when processing all newsgroups.
**	V6.1b10	02-Dec-1996     Mark Pizzolato  mark@infocomm.com
**	  - Changed file delete logic for SKIM/ITEMS to actually delete 
**	    files in several AST threads that run in parallel with other SKIM
**	    processing.  This allows for much more overlapped I/O (even 
**	    overlapped I/O for file deletes since we will potentially be
**	    deleting files for up to n different newsgroups at any one time,
**	    n in this case is specified by the user with SKIM/ITEMS=n, n 
**	    defaults to 5, A value of 0 for n disables this AST based 
**          delete processing).
**	V6.1b11	28-Feb-1997	Mark Martinec   mark.martinec@nsc.ijs.si
**	  - remove unused variable list from asynch_search_complete()
**	    and asynch_parse_complete()
**	V6.1b11 14-Apr-1997	Mark Martinec   mark.martinec@ijs.si
**	  - replace several calls to strcpy with safer strnncpy
**	  - replace pattern strcpy(news_malloc(strlen(x)+1),x) with news_strdup
**--
**/

#ifdef vaxc
#module NEWSSKIM "V6.2"
#endif

#define _NEWSSKIM_C
#define module_name "NEWSSKIM"

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

#if NNTP_CLIENT_ONLY

int do_skim() { return(0); }

#else

#if NAKED_INCLUDES
#include sordef
#include stsdef
#else
#include <sordef.h>
#include <stsdef.h>
#endif

#define	 SOR$M_NODUPS              0x40

extern
int mailfile_open;

static
struct {
    short numkeys,
          keytype,
          keyorder,
          keyoffset,
          keylength;
    } keys = {1, DSC$K_DTYPE_T, 0, 0, IDLEN};

static
int served_cmd = 1,
    skim_all;

static
struct sk {
    unsigned int sk_num;
    struct sk *sk_next;
    char sk_name[256];
    } *sk_head = 0,
      *sk_expanded = 0;

static
struct gl_list {
    unsigned int gl_num;
    struct gl_list *gl_next;
    } *gl_head = 0, *ns_head = 0;

extern int sor$begin_sort(), sor$release_rec(), sor$sort_merge(),
	   sor$return_rec(), sor$end_sort();

/*
 *  add_check_id
 *
 *  Add item identifier to list to check against items held on server node
 */

static
struct grps *check_id_head = 0;

static
struct itms *irecheck = 0;

static void add_recheck(id)
  char *id;
{
  struct itms *rc;

  for (rc=irecheck; rc; rc=rc->itmsnext) {
    if (!strncmp(rc->itmid,id,IDLEN)) return;
    }
  rc = news_malloc(sizeof *rc);
  util_idcpy(rc->itmid,id);
  rc->itmsize = 0;
  rc->itmsnext = irecheck;
  irecheck = rc;
}

void add_check_id(gn,ii,ig,ng,etime,proto)
  char *gn, *ii, *ng;
  unsigned int ig;
  int etime, proto;
{
  struct gl_list *gt;
  struct grps *tmp;
  struct itms *itmp;

  for (gt=gl_head;  gt && (gt->gl_num != ig);  gt=gt->gl_next) {}
  if (gt) return;
  for (tmp=check_id_head;  tmp && (tmp->itmgrp != ig);  tmp=tmp->grpsnext) {}
  if (!tmp) {
    tmp = (struct grps *) news_malloc(sizeof *tmp);
    strnncpy(tmp->grpname,sizeof(tmp->grpname), gn,-1, 0);  /* gn may be NULL */
    strnncpy(tmp->grpnode,sizeof(tmp->grpnode), ng,-1, 0);  /* ng may be NULL */
    tmp->itmgrp = ig;
    tmp->ids = tmp->idr = 0;
    tmp->grptime = etime;
    tmp->grpsnext = check_id_head;
    tmp->nproto = proto;
    check_id_head = tmp;
    }
  if (ii) {
    itmp = (struct itms *) news_malloc(sizeof *itmp);
    util_idcpy(itmp->itmid,ii);
    itmp->itmsnext = tmp->ids;
    tmp->ids = itmp;
    }
}

void
clear_gl()
{
  struct gl_list *gt;

  while (gl_head) {
    gt = gl_head;
    gl_head = gl_head->gl_next;
    news_free(gt);
    }
}

void server_check(reset,served_cmd)
  int reset, served_cmd;
{
  int status;
  struct grps *ig = check_id_head;
  struct itms *it, *ip, *ic, *icp;
  char itmkey[IDLEN + 4], out_line[132];
  unsigned int *idgrp;
  int del_remainder, s_smg_active = 0, add_count, del_count;
  struct gl_list *gt;

  if (check_id_head) {
    if (smg_active) {
      noscreen();
      s_smg_active = 1;
      }
    newsgrp.grp_num = 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;

    idgrp = (unsigned int *) &itmkey[IDLEN];
    itmrab.rab$l_kbf = itmkey;
    itmrab.rab$b_krf = 1;
    itmrab.rab$b_ksz = IDLEN + 4;
    itmrab.rab$l_rop = RAB$M_WAT ;
    itmrab.rab$b_rac = RAB$C_KEY;

    if (served_cmd)
      server_check_ids(check_id_head);
    while (check_id_head) {
      gt = (struct gl_list *) news_malloc(sizeof *gt);
      gt->gl_num = check_id_head->itmgrp;
      gt->gl_next = gl_head;
      gl_head = gt;
      del_remainder = 0;
      ig = check_id_head;
      strnncpy(out_line,sizeof(out_line), ig->grpname,-1, 0);
      add_count = 0;
      del_count = 0;
      if (check_id_head->idr) {
        grprab.rab$l_kbf = (char *) &(ig->itmgrp);
        if (!sys_get_nornf(&grprab)) newsgrp.grp_num = 0;
        }
      if (((it = check_id_head->idr) != 0) && !it->itmsize && it->itmdate) {
        del_remainder = 1;
        while ((it = check_id_head->idr) != 0) {
          ip = 0;
          for (; it->itmsnext; it=it->itmsnext) ip = it;
          icp = 0; ic = check_id_head->ids;
          while (ic && strncmp(ic->itmid,it->itmid,IDLEN)) {
            icp = ic; ic = ic->itmsnext;
            }
          if (ic) {
            if (icp) icp->itmsnext = ic->itmsnext;
            else check_id_head->ids = ic->itmsnext;
            news_free(ic);
            news_free(it);
            }
          else {
            newsitm.itm_recvdate = newsitm.itm_postdate = it->itmdate;
            if (it->itmsize) newsitm.itm_flags = NEWS_M_LINESVALID;
            else newsitm.itm_flags = 0;
            newsitm.itm_lines = it->itmsize;
            newsitm.itm_life = 0;
            newsitm.itm_cachedate = 0;
            util_subjcpy(newsitm.itm_title,"-- Title not Obtained --");
            newsitm.itm_from[0] = '\0';
            util_idcpy(newsitm.itm_id,it->itmid);
            if (!hist_check(newsitm.itm_id)) {
              newsitm.itm_grp = ig->itmgrp;
              newsitm.itm_num = ++newsgrp.grp_topnum;
              time((time_t *) &newsgrp.grp_entdate);
              itmrab.rab$w_rsz = sizeof newsitm;
              itmrab.rab$l_rbf = (char *) &newsitm;
              if (!sys_put(&itmrab))
                --newsgrp.grp_topnum;
              else {
                ++newsgrp.grp_count;
                ++add_count;
                add_recheck(newsitm.itm_id);
                }
              }
            news_free(it);
            }
          if (ip) ip->itmsnext = 0;
          else check_id_head->idr = 0;
          }
        }
      else {
        while ((it = check_id_head->idr) != 0) {
          ip = 0;
          for (; it->itmsnext; it=it->itmsnext) ip = it;
          if (it->itmdate) {
            newsitm.itm_recvdate = newsitm.itm_postdate = it->itmdate;
            if (it->itmsize) newsitm.itm_flags = NEWS_M_LINESVALID;
            else newsitm.itm_flags = 0;
            newsitm.itm_lines = it->itmsize;
            newsitm.itm_life = 0;
            newsitm.itm_cachedate = 0;
            util_subjcpy(newsitm.itm_title,it->itmtitle);
            util_idcpy(newsitm.itm_id,it->itmid);
            if (!hist_check(newsitm.itm_id)) {
              newsitm.itm_grp = ig->itmgrp;
              newsitm.itm_num = ++newsgrp.grp_topnum;
              time((time_t *) &newsgrp.grp_entdate);
              itmrab.rab$w_rsz = sizeof newsitm;
              itmrab.rab$l_rbf = (char *) &newsitm;
              time((time_t *) &newsgrp.grp_entdate);
              if (!sys_put(&itmrab))
                --newsgrp.grp_topnum;
              else {
                ++newsgrp.grp_count;
                ++add_count;
                }
              }
            }
          else {
            util_idcpy(itmkey,it->itmid);
            *idgrp = ig->itmgrp;
            if (sys_get_nornf(&itmrab)) {
              sys_delete(&itmrab);
              hist_add(newsitm.itm_id);
              --newsgrp.grp_count;
              ++del_count;
              }
            }
          if (ip) ip->itmsnext = 0;
          else check_id_head->idr = 0;
          news_free(it);
          }
        }
      if (irecheck) {
        server_get_titles(check_id_head,irecheck);
        itmrab.rab$l_kbf = itmkey;
        itmrab.rab$b_krf = 1;
        itmrab.rab$b_ksz = IDLEN + 4;
        itmrab.rab$l_rop = RAB$M_WAT ;
        itmrab.rab$b_rac = RAB$C_KEY;
        }
      while (irecheck) {
        it = irecheck;
        if (*it->itmid) {
          util_idcpy(itmkey,it->itmid);
          *idgrp = check_id_head->itmgrp;
          if (sys_get_nornf(&itmrab)) {
            if (it->itmvalid) {
              util_subjcpy(newsitm.itm_title,it->itmtitle);
              util_fromcpy(newsitm.itm_from,it->itmfrom);
              newsitm.itm_flags |= NEWS_M_LINESVALID;
              newsitm.itm_lines = it->itmsize;
	      newsitm.itm_recvdate = newsitm.itm_postdate = it->itmdate;
              sys_update(&itmrab);
	      }
	    else {
              sys_delete(&itmrab);
              --newsgrp.grp_count;
	      }
            }
          }
        irecheck = it->itmsnext;
        news_free(it);
        }
      for (it=check_id_head->ids; it; ) {
        if (del_remainder) {
          util_idcpy(itmkey,it->itmid);
          *idgrp = ig->itmgrp;
          if (sys_get_nornf(&itmrab)) {
            sys_delete(&itmrab);
            hist_add(newsitm.itm_id);
            ++del_count;
            --newsgrp.grp_count;
            }
          }
        it = it->itmsnext;
        news_free(check_id_head->ids);
        check_id_head->ids = it;
        }
      if (newsgrp.grp_num) sys_update(&grprab);
      ig = ig->grpsnext;
      news_free(check_id_head);
      check_id_head = ig;

      if (verbose && (add_count || del_count))
        printf("\t%s - %d items added, %d items deleted\n",
               out_line,add_count,del_count);
      }
    if (s_smg_active) smg_active = news_context;
    if (reset) mem_reset(cur_dir_type,1);
    }
  return;
}

/*
 * error checking macros for use during skim - force initial condition to
 * severity I, so that we don't abort the skim pass.
 * 26-May-1994  Charles Bailey  bailey@genetics.upenn.edu
 */

#define _ck_error_fp_info(sts,file_ptr) \
  (((file_ptr) && !ferror(file_ptr)) ? 1 : \
   (report_cio_error_routine((sts & ~STS$M_SEVERITY) | STS$K_INFO, \
                             file_ptr,0,module_name_str,__LINE__),0))

#define _ck_error_fn_info(sts,file_name) \
  ((!errno) ? 1 : \
   (report_cio_error_routine((sts & ~STS$M_SEVERITY) | STS$K_INFO, \
                             0,file_name,module_name_str,__LINE__),0))

#define _ck_close_info(file_ptr) _ck_error_fp_info(NEWS$_CLOFAIL,file_ptr)
#define _ck_delete_info(file_name) _ck_error_fn_info(NEWS$_REMFAIL,file_name)
#define _ck_open_info(file_name) _ck_error_fn_info(NEWS$_OPNFAIL,file_name)
#define _ck_put_info(file_ptr) _ck_error_fp_info(NEWS$_PUTFAIL,file_ptr)

/*
 *  files_skim
 *
 *  Delete all unreferenced ITM files in NEWS area
 */

static void files_skim()
{
  int status;
  char fnam[257], grp[GRPNAMELEN+100], lstgrp[132], ifiles[250],
       *sf, *p;
  unsigned int itm_key[2], g = 0, m, last_g = 0, last_m = 0, no_grp = 0;
  $DESCRIPTOR(fnam_dsc,fnam);
  $DESCRIPTOR_CONST(itm_dsc,ifiles);
  int context = 0, version, del_count = 0;
  struct sk *tmp = 0;

  *lstgrp = '\0';
  itmrab.rab$l_kbf = (char *) itm_key;
  itmrab.rab$b_krf = 0;
  itmrab.rab$b_ksz = 8;
  itmrab.rab$l_rop= RAB$M_RRL | RAB$M_NLK ;
  itmrab.rab$b_rac = RAB$C_KEY;

  grprab.rab$l_kbf = grp;
  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 (skim_all) strnncpy(ifiles,sizeof(ifiles), Itm_files,-1, 0);
  else {
    tmp = sk_expanded;
    sprintf(ifiles,Itmg_template,tmp->sk_name);
    }

  itm_dsc.dsc$w_length = strlen(ifiles);
  for (;;) {
    status = lib$find_file(&itm_dsc,&fnam_dsc,&context,0,0,0,0);
    if (skim_all) {
      if (!(status & 1)) break;
      }
    else {
      if (!(status & 1)) {
        if (!(tmp = tmp->sk_next)) break;
        lib$find_file_end(&context);
        sprintf(ifiles,Itmg_template,tmp->sk_name);
        itm_dsc.dsc$w_length = strlen(ifiles);
        continue;
        }
      }

    fnam[256] = '\0';
    chop_str(fnam,' ');
    if (!(sf = strrchr(fnam,':'))) continue;
    if (!(sf = strrchr(fnam,'['))) continue;
    ++sf;
    if (!(p = strchr(sf,']'))) continue;
    *p = ' ';
    if (*sf == '0') {
      if (!(sf = strchr(sf,'.'))) continue;
      sf++;
      }
    if (sscanf(sf,"%s %d.ITM;%d",grp,&m,&version) != 3) {
      *p = ']';
      printf("\tUnrecognised news item file - ?%s?\n",fnam);
      continue;
      }
    *p = ']';
    strnncpy(grp,sizeof(grp), util_undir(grp),-1, 0);
    util_cvrt(grp,grp);

    if (strcmp(grp,lstgrp)) {
      if (verbose && del_count)
        printf("\t%s - %d files deleted\n",lstgrp,del_count);
      del_count = 0;
      util_cvrt(lstgrp,grp);
      if (!sys_get_nornf(&grprab)) {
        printf("\tCannot match directory to groupname - %s\n",lstgrp);
        no_grp = 1;
        last_g = 0;
        }
      else {
        no_grp = 0;
        g = newsgrp.grp_num;
        }
      }
    if (no_grp) {
      ++del_count;
      if (delete(fnam)) _ck_delete_info(fnam);
      continue;
      }
    else {
      itm_key[0] = m;
      itm_key[1] = g;
      if ((last_g == itm_key[1]) && (last_m == itm_key[0])) {
        if (verbose) printf("\tDelete %s - backup version?\n",fnam);
        ++del_count;
        if (delete(fnam)) _ck_delete_info(fnam);
        continue;
        }
      if (!sys_get_nornf(&itmrab)) {
        ++del_count;
        if (delete(fnam)) _ck_delete_info(fnam);
        }
      last_g = itm_key[1];
      last_m = itm_key[0];
      }
    }
  lib$find_file_end(&context);
  printf("\n");
}

/*
 *  dir_skim
 *
 *  Delete all Sub-Directories which are no longer valid
 */

static void dir_skim()
{
  int status;
  char gfiles[250], gnam[257], grp[GRPNAMELEN+100], *p, *ext, *sf;
  int context = 0, i, j, version;
  time_t t;
  $DESCRIPTOR_CONST(grp_dsc,gfiles);
  $DESCRIPTOR(gnam_dsc,gnam);

  grprab.rab$l_kbf = grp;
  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 (!skim_all) return;

  sysprv();
  for (i = 7; i >= 0 ; --i) {
    strcpy(gfiles,"NEWS_DEVICE:[000000");
    for (j = 0 ; j < i ; ++j) strcat(gfiles,".*");
    strcat(gfiles,"]*.DIR;*");

    grp_dsc.dsc$w_length = strlen(gfiles);
    for (;;) {
      if (!(lib$find_file(&grp_dsc,&gnam_dsc,&context,0,0,0,0) & 1)) break;
      gnam[256] = '\0';
      chop_str(gnam,' ');
      if (!(sf = strrchr(gnam,':'))) continue;
      if (!(sf = strrchr(gnam,'['))) continue;
      sf += 7;
      if (*sf == ']') p = sf;
      else if (!(p = strchr(sf,']'))) continue;
      ++sf;
      *p = ' ';
      if (!(ext = strrchr(p,'.'))) continue;
      *ext = ' ';
      *p = '.';
      if (sscanf(sf,"%s DIR;%d",grp,&version) != 2) {
        *p = ']';
        *ext = '.';
        continue;
        }
      if (version != 1) {
        *p = ']';
        *ext = '.';
        if (verbose) {
          printf("\t\t.DIR file with version greater then 1\n");
          printf("\t\t%s - DELETED\n",gnam);
          }
        chmod(gnam,0755);
        if (delete(gnam)) _ck_delete_info(gnam);
        continue;
        }
      strnncpy(grp,sizeof(grp), util_undir(grp),-1, 0);
      util_cvrt(grp,grp);
      if ( verbose ) {
        t=time(0);
        printf("\t\tProcessing %s (%8.8s)\n",grp,ctime(&t)+11);
        }
      if (!*grp) {
        *p = ']';
        *ext = '.';
        continue;
        }
      if (!sys_get_nornf(&grprab)) {
        strcat(grp,".");
        grprab.rab$b_ksz = strlen(grp);
        if (sys_get_nornf(&grprab) &&
            !strncmp(util_grp_name_str(newsgrp.grp_name),grp,strlen(grp))) {
          grprab.rab$b_ksz = GRPNAMELEN_F;
          continue;
          }
        grprab.rab$b_ksz = GRPNAMELEN_F;
        *p = ']';
        *ext = '.';
        chmod(gnam,0755);
        if (!delete(gnam)) {
          if (verbose) printf("\t\t%s Deleted - no newsgroup\n",gnam);
          }
        else {
          grp[strlen(grp) - 1] = '\0';
          sprintf(itm_fname,Access_template,util_dir(grp));
          while (!delete(itm_fname)) ;
          if (errno != ENOENT) _ck_delete_info(itm_fname);
          if (!delete(gnam)) {
            if (verbose) printf("\t\t%s Deleted - no newsgroup\n",gnam);
            }
          else {
            _ck_delete_info(gnam);
            if (verbose)
              printf("\t\t%s Not deleted - directory not empty or protected\n",gnam);
            }
          }
        }
      }
    lib$find_file_end(&context);
    context = 0;
    }
  printf("\n");
  nosysprv();
}

/*
 *  grp_skim
 *
 *  Delete all empty old groups (GRP_TIME life time after last item entered
 */

static void grp_skim()
{
  int status;
  time_t cur_time,t;
  int glob_exp_time,
      grp_exp_time,
      glob_iexp_time,
      grp_iexp_time,
      itm_count,
      grp_counter = 0,
      grpfnum;
  unsigned int itm_key[2];
  unsigned short glob_exp_val,
                 grp_exp_val,
                 glob_iexp_val,
                 grp_iexp_val;
  struct sk *tmp = 0;
  FILE *fpr;

  time(&cur_time);
  if ( daymod ) cur_time -= (cur_time % DAY_SECS);

  grprab.rab$l_kbf = (char *) c$rfi(0);
  grprab.rab$b_ksz = 4;
  grprab.rab$b_krf = 1;
  grprab.rab$l_rop = RAB$M_RRL | RAB$M_NLK;
  grprab.rab$b_rac = RAB$C_KEY;

  glob_exp_val = GRP_TIME ;
  glob_iexp_val = EXP_TIME ;
  if (sys_get_nornf(&grprab)) {
    if (newsgrp.grp_life) glob_exp_val = newsgrp.grp_life ;
    if (newsgrp.grp_itmlife) glob_iexp_val = newsgrp.grp_itmlife ;
    }
  if (glob_exp_val == 65535) glob_exp_time = 0;
  else glob_exp_time = cur_time - (glob_exp_val * DAY_SECS);
  if (glob_iexp_val == 65535) glob_iexp_time = 0;
  else glob_iexp_time = cur_time - (glob_iexp_val * DAY_SECS);

  itmrab.rab$l_kbf = (char *) itm_key;
  itmrab.rab$l_rop= RAB$M_RRL | RAB$M_KGE | RAB$M_NLK;
  itmrab.rab$b_rac = RAB$C_KEY;
  itmrab.rab$b_krf = 0;
  itmrab.rab$b_ksz = 8;

  if (skim_all) {
    grprab.rab$b_krf = 0;
    grprab.rab$b_rac = RAB$C_SEQ;
    sys_rewind(&grprab);
    }
  else {
    tmp = sk_expanded;
    grprab.rab$l_kbf = (char *) &(tmp->sk_num);
    grprab.rab$b_ksz = 4;
    grprab.rab$b_krf = 1;
    grprab.rab$b_rac = RAB$C_KEY;
    }
  grprab.rab$l_rop = RAB$M_WAT;

  for (;;) {
    sys_get_nornf(&grprab);
    if (skim_all) {
      if (!(status & 1)) break;
      }
    else {
      if (!(status & 1)) {
        if (!(tmp = tmp->sk_next)) break;
        grprab.rab$l_kbf = (char *) &(tmp->sk_num);
        }
      }

    if (!newsgrp.grp_num) continue;
    err_oline[0] = '\0'; err_oline_cat(util_grp_name_str(newsgrp.grp_name));
    if ((grp_iexp_val = newsgrp.grp_itmlife) != 0) {
      if (grp_iexp_val == 65535) grp_iexp_time = 0;
      else grp_iexp_time = cur_time - (grp_iexp_val * DAY_SECS);
      }
    else {
      grp_iexp_val = glob_iexp_val ;
      grp_iexp_time = glob_iexp_time ;
      }
    if ((newsgrp.grp_flags & NEWS_M_NNTPSRV) && served_cmd)
      add_check_id(util_grp_name_str(newsgrp.grp_name),0,
                   newsgrp.grp_num,newsgrp.grp_srvnode,
                   grp_iexp_time,newsgrp.grp_srvproto);
    itm_count = grpfnum = 0;
    itm_key[0] = 0;
    itm_key[1] = newsgrp.grp_num;
    itmrab.rab$l_kbf = (char *) itm_key;
    itmrab.rab$l_rop= RAB$M_RRL | RAB$M_KGE | RAB$M_NLK;
    itmrab.rab$b_rac = RAB$C_KEY;
    itmrab.rab$b_krf = 0;
    itmrab.rab$b_ksz = 8;

    while (sys_get_nornf(&itmrab)) {
      itmrab.rab$l_rop = RAB$M_RRL | RAB$M_NLK ;
      itmrab.rab$b_rac = RAB$C_SEQ;
      if (newsitm.itm_grp != newsgrp.grp_num) break;
      else {
        ++itm_count;
        if (!grpfnum) grpfnum = newsitm.itm_num;
        }
      }
    itmrab.rab$l_rop= RAB$M_RRL | RAB$M_KGE | RAB$M_NLK;
    itmrab.rab$b_rac = RAB$C_KEY;
    if (verbose) {
      t=time(0);
      printf("\t%s - %d items (%8.8s)\n",err_oline,itm_count,ctime(&t)+11);
      }
    if (!grpfnum) grpfnum = newsgrp.grp_topnum + 1;
    if (   (itm_count != newsgrp.grp_count)
        || (grpfnum != newsgrp.grp_firstnum)
       ) {
      newsgrp.grp_firstnum = grpfnum;
      newsgrp.grp_count = itm_count;
      sys_update(&grprab);
      }
    if (!itm_count) {
      if ((grp_exp_val = newsgrp.grp_life) != 0) {
        if (grp_exp_val == 65535) grp_exp_time = 0;
        else grp_exp_time = cur_time - (grp_exp_val * DAY_SECS);
        }
      else {
        grp_exp_val = glob_exp_val ;
        grp_exp_time = glob_exp_time ;
        }
      if ((newsgrp.grp_entdate < grp_exp_time) && !(newsgrp.grp_flags & NEWS_M_NNTPSRV)) {
        if (verbose) printf("\t\tNewsgroup DELETED\n");
        sys_delete(&grprab);
        sprintf(itm_fname,Access_template,util_dir(newsgrp.grp_name));
        sysprv();
        if ((fpr = fopen(itm_fname,"r")) != 0) {
          if (fclose(fpr)) _ck_close_info(fpr);
          while (!delete(itm_fname)) ;
          if (errno != ENOENT) _ck_delete_info(itm_fname);
          }
        nosysprv();
        continue;
        }
      }
    ++grp_counter;
    
    if (check_id_head) {
      unsigned int sav_contxt = newsgrp.grp_num;

      server_check(0,served_cmd);

      grprab.rab$b_ksz = 4;
      grprab.rab$b_krf = 1;
      grprab.rab$b_rac = RAB$C_KEY;
      if (!skim_all)
        grprab.rab$l_kbf = (char *) &(tmp->sk_num);
      else {
	grprab.rab$l_kbf = (char *) &sav_contxt;
        sys_get(&grprab);
        grprab.rab$b_rac = RAB$C_SEQ;
        }
      itmrab.rab$l_kbf = (char *) itm_key;
      itmrab.rab$l_rop= RAB$M_RRL | RAB$M_KGE | RAB$M_NLK;
      itmrab.rab$b_rac = RAB$C_KEY;
      itmrab.rab$b_krf = 0;
      itmrab.rab$b_ksz = 8;
      }
    if (!skim_all) { 
      if (!(tmp = tmp->sk_next)) break;
      grprab.rab$l_kbf = (char *) &(tmp->sk_num);
      }
    }
  if (skim_all) {
    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 = grp_counter;
    sys_update(&grprab);
    }
  printf("\n");
}

/*
 *  do_skim
 *
 *  Open the item and subject files for sharing
 */

static
struct dlist {
        char *dg;
        int di;
        struct dlist *dn;
        } *dh = 0;

static
char dgroup[GRPNAMELEN];

static
struct gdlist {
        int di;
        struct gdlist *dn;
        } *gdh = 0, *free_gdh = 0;

static
struct group_dlist {
	struct group_dlist *dn;
	struct FAB dfab;
	struct NAM dnam;
	char fna[NAM$C_MAXRSS+1];
	char esa[NAM$C_MAXRSS+1];
	char rsa[NAM$C_MAXRSS+1];
	char dg[GRPNAMELEN];
	struct gdlist *dh;
	} *gdl = 0, *free_gdl = 0;
static
int max_parallel_deletes = 0;
static
int parallel_deletes_active = 0;
static
int parallel_directories_queued = 0, parallel_peak_directories_queued = 0;
static
int parallel_files_queued = 0, parallel_peak_files_queued = 0;
static
int parallel_wait_efn = 0;

static void
asynch_delete_files(struct FAB *fab);

static void
asynch_search_complete(struct FAB *fab)
{
  unsigned int msysprv[2], prvprv[2];
  int status;

  msysprv[0] = PRV$M_SYSPRV;
  msysprv[1] = 0;
  sys$setprv(1, msysprv, 0, prvprv);
  status = sys$erase(fab, asynch_delete_files, asynch_delete_files);
  if (0 == (PRV$M_SYSPRV&prvprv[0]))
    sys$setprv(0, msysprv, 0, 0);
}

static void
asynch_parse_complete(struct FAB *fab)
{
  int status;

  status = sys$search(fab, asynch_delete_files, asynch_search_complete);
}

static void
asynch_delete_files(struct FAB *fab)
{
  struct group_dlist *list = (struct group_dlist *)fab->fab$l_ctx;
  int status;
  struct group_dlist *group_dt;
  struct gdlist *dt;

  if ((RMS$_NORMAL == (status = fab->fab$l_sts)) && 
      ((list->dnam.nam$b_ver != 2) ||
       (list->dnam.nam$l_ver[1] != '1'))) {
    /*
    ** This handles the case where we just successfully deleted a file
    ** that wasn't version ";1", hence it is likely there is another, so
    ** start over parsing the same file spec again (the spec doesn't specify 
    ** a version.
    */
    status = sys$search(fab, asynch_delete_files, asynch_search_complete);
    return;
    }
  if (list->dh) {
    sprintf(list->fna, Itm_template, list->dg, list->dh->di);
    list->dfab.fab$b_fns = strlen(list->fna);
    dt = list->dh->dn;
    --parallel_files_queued;
    list->dh->dn = free_gdh;
    free_gdh = list->dh;
    list->dh = dt;
    status = sys$parse(fab, asynch_delete_files, asynch_parse_complete);
    }
  else {
    if (gdl != list) {
      for (group_dt = gdl; group_dt->dn != list; group_dt = group_dt->dn);
      group_dt->dn = list->dn;
      }
    else
      gdl = list->dn;
    list->dn = free_gdl;
    free_gdl = list;
    --parallel_directories_queued;
    for (group_dt = gdl; (group_dt != NULL) && (group_dt->fna[0] != '\0'); group_dt = group_dt->dn);
    if (NULL != group_dt)
      asynch_delete_files(&group_dt->dfab);
    else {
      if (0 == (--parallel_deletes_active))
	sys$setef(parallel_wait_efn);
      }
    }
}

static void
start_asynch_directory_delete(char *dgroup)
{
  struct group_dlist *group_dt;
  
  sys$setast(0);
  if (NULL == (group_dt = free_gdl))
    group_dt = (struct group_dlist *) news_malloc(sizeof *gdl);
  else
    free_gdl = group_dt->dn;
  strnncpy(group_dt->dg, sizeof(group_dt->dg), util_dir(dgroup),-1, 0);
  group_dt->dfab = cc$rms_fab;
  group_dt->dnam = cc$rms_nam;
  group_dt->dfab.fab$l_nam = &group_dt->dnam;
  group_dt->dfab.fab$l_fop |= (FAB$M_ASY|FAB$M_NAM);
  group_dt->dfab.fab$l_fna = &group_dt->fna[0];
  group_dt->dfab.fab$l_dna = ";*";
  group_dt->dfab.fab$b_dns = 2;
  group_dt->dfab.fab$l_ctx = (unsigned long)group_dt;
  group_dt->dnam.nam$l_esa = &group_dt->esa[0];
  group_dt->dnam.nam$b_ess = NAM$C_MAXRSS;
  group_dt->dnam.nam$l_rsa = &group_dt->rsa[0];
  group_dt->dnam.nam$b_rss = NAM$C_MAXRSS;
  memset(group_dt->fna, '\0', sizeof(group_dt->fna));
  group_dt->dh = gdh;
  gdh = NULL;
  group_dt->dn = gdl;
  gdl = group_dt;
  ++parallel_directories_queued;
  if (parallel_peak_directories_queued < parallel_directories_queued)
    parallel_peak_directories_queued = parallel_directories_queued;
  if (max_parallel_deletes > parallel_deletes_active) {
    if (0 == parallel_wait_efn)
      lib$get_ef(&parallel_wait_efn);
    sys$clref(parallel_wait_efn);
    ++parallel_deletes_active;
    asynch_delete_files(&group_dt->dfab);
    }
  sys$setast(1);
}

static int doskim()
{
  int status;
  time_t cur_time,t,ot,et;
  int del_all_itms = 0, itm_count = 0, glob_exp_time, grp_exp_time = 0,
      itm_exp_time, s_smg_active = 0, sor_start = 0, fchk = 1, hist_date,
      mfo = mailfile_open, itm_skim_count, flk;
  unsigned int itm_key[2], cur_grp = 0;
  unsigned short glob_exp_val, grp_exp_val = 0, itm_exp_val,
		 arch_len, newsgroups_len;
  short a_len;
  char arch_file[256], sor_line[512], newsgroups[132], out_line[256];
  FILE *fpr;
  struct dlist *dt;
  struct gdlist *gdt;
  struct sk *tmp = 0, *extmp;
  $DESCRIPTOR(a_dsc,sor_line);
  $DESCRIPTOR(arch_dsc,arch_file);
  $DESCRIPTOR(newsgroups_dsc,newsgroups);

  *out_line = '\0';
  itm_skim_count = 0;
  if (no_priv()) return(err_line("Error: Skim - no privs for operation"),0);

  if (mfo) do_close_mail();
  while (sk_head) {
    tmp = sk_head;
    if (!(sk_head = sk_head->sk_next)) sk_head = sk_expanded;
    news_free(tmp);
    }
  sk_expanded = 0;

  if (cli$present(c$dsc("FILECHECK")) == CLI$_NEGATED) fchk = 0;
  verbose = 1;
  if (cli$present(c$dsc("VERBOSE")) == CLI$_NEGATED) verbose = 0;
  daymod = 1;
  if (cli$present(c$dsc("DAY")) == CLI$_NEGATED) daymod = 0;

  if (cli$get_value(c$dsc("GROUPS"),&newsgroups_dsc,&newsgroups_len) & 1) {
    do {
      newsgroups[newsgroups_len] = '\0';
      util_cvrt(newsgroups,newsgroups);
      tmp = (struct sk *) news_malloc(sizeof *tmp);
      tmp->sk_next = sk_head;
      strnncpy(tmp->sk_name,sizeof(tmp->sk_name), newsgroups,-1, 0);
      sk_head = tmp;
      } while (cli$get_value(c$dsc("GROUPS"),&newsgroups_dsc,&newsgroups_len) & 1);
    grprab.rab$b_krf = 0;
    grprab.rab$l_rop = RAB$M_RRL | RAB$M_NLK;
    grprab.rab$b_rac = RAB$C_SEQ;
    sys_rewind(&grprab);
    skim_all = 1;
    while (sys_get_noeof(&grprab)) {
      int fnd = 0;

      if (!newsgrp.grp_num) continue;
      for (tmp=sk_head; tmp; tmp=tmp->sk_next) {
        if (wild_match(util_grp_name_str(newsgrp.grp_name),tmp->sk_name)) {
          fnd = 1;
          extmp = sk_expanded;
          while ((extmp) && (extmp->sk_num != newsgrp.grp_num)) extmp = extmp->sk_next;
          if (!extmp) {
            extmp = (struct sk *) news_malloc(sizeof *extmp);
            extmp->sk_next = sk_expanded;
            strnncpy(extmp->sk_name,sizeof(extmp->sk_name),
                     newsgrp.grp_name,util_grp_name_len(newsgrp.grp_name), 0);
            extmp->sk_num = newsgrp.grp_num;
            sk_expanded = extmp;
            }
          break;
          }
        }
      if (!fnd) skim_all = 0;
      }
    while (sk_head) {
      tmp = sk_head;
      if ((!(sk_head = sk_head->sk_next)) && (skim_all)) {
        sk_head = sk_expanded;
        sk_expanded = 0;
        }
      news_free(tmp);
      }
    }
  else {
    skim_all = 1;
    grprab.rab$b_krf = 0;
    grprab.rab$l_rop = RAB$M_RRL | RAB$M_NLK;
    grprab.rab$b_rac = RAB$C_SEQ;
    sys_rewind(&grprab);
    while (sys_get_noeof(&grprab)) {
      if (!newsgrp.grp_num) continue;
      extmp = (struct sk *) news_malloc(sizeof *extmp);
      extmp->sk_next = NULL;
      strnncpy(extmp->sk_name,sizeof(extmp->sk_name),
               newsgrp.grp_name,util_grp_name_len(newsgrp.grp_name), 0);
      extmp->sk_num = newsgrp.grp_num;
      if (NULL != (tmp = sk_expanded)) {
	while (tmp->sk_next) tmp = tmp->sk_next;
	tmp->sk_next = extmp;
	}
      else sk_expanded = extmp;
      }
    }

  if ((!skim_all) && (!sk_expanded)) {
    if (mfo) do_open_mail();
    return(err_line("Warning: SKIM - No newsgroups to scan"),0);
    }

  if (smg_active) {
    noscreen();
    s_smg_active = 1;
    }

  served_cmd = 0;
  if (cli$present(c$dsc("SERVED")) != CLI$_NEGATED) {
    grprab.rab$b_krf = 0;
    grprab.rab$b_rac = RAB$C_SEQ;
    grprab.rab$l_rop = RAB$M_NLK | RAB$M_RRL;
    sys_rewind(&grprab);
    do {
      if (   sys_get_noeof(&grprab)
          && newsgrp.grp_num 
          && (newsgrp.grp_flags & NEWS_M_NNTPSRV))
        break;
      } while (status & 1);
    if (status & 1) served_cmd = 1;
    else printf("\nSKIM/SERVED - No remote served newsgroups - /NOSERVED used\n");
    }

  if (cli$present(c$dsc("ITEMS")) == CLI$_NEGATED)
    printf("\nSKIM/NOITEMS - skipping item scan\n");
  else {
    cli$get_value(c$dsc("ITEMS"),&arch_dsc,&arch_len);
    arch_file[arch_len] = '\0';
    if (!sscanf(arch_file,"%d",&max_parallel_deletes)) max_parallel_deletes = 0;
    if (max_parallel_deletes)
      printf("\nSKIM/ITEMS=%d - item scan:\n", max_parallel_deletes);
    else
      printf("\nSKIM/ITEMS - item scan:\n");

    dh = 0;
    if (cli$present(c$dsc("ARCHIVE")) & 1) {
      cli$get_value(c$dsc("ARCHIVE"),&arch_dsc,&arch_len);
      arch_file[arch_len] = '\0';
      }
    else *arch_file = '\0';

    glob_exp_val = EXP_TIME ;
    time(&cur_time);
    if ( daymod )cur_time -= (cur_time % DAY_SECS);
    newsgrp.grp_num = 0;

    grprab.rab$l_kbf = (char *) &cur_grp;
    grprab.rab$b_ksz = 4;
    grprab.rab$b_krf = 1;
    grprab.rab$l_rop = RAB$M_WAT;
    grprab.rab$b_rac = RAB$C_KEY;

    if ((sys_get_nornf(&grprab)) && newsgrp.grp_itmlife)
      glob_exp_val = newsgrp.grp_itmlife ;
    if (glob_exp_val == 65535) glob_exp_time = 0;
    else glob_exp_time = cur_time - (glob_exp_val * DAY_SECS);

    if ((skim_all) && (!sk_expanded)) {
      itmrab.rab$b_rac = RAB$C_SEQ;
      itmrab.rab$b_krf = 0;
      itmrab.rab$l_rop = RAB$M_WAT;
      sys_rewind(&itmrab);
      }
    else {
      tmp = sk_expanded;
      itm_key[0] = 0;
      itm_key[1] = tmp->sk_num;
      itmrab.rab$b_krf = 0;
      itmrab.rab$b_ksz = 8;
      itmrab.rab$l_kbf = (char *) itm_key;
      itmrab.rab$b_rac = RAB$C_KEY;
      itmrab.rab$l_rop = RAB$M_WAT | RAB$M_KGE;
      }

    ot = time(0);
    for (;;) {
      sys_get_nornf(&itmrab);
      if ((skim_all) && (!sk_expanded))
        { if (!(status & 1)) break; }
      else {
        itmrab.rab$b_rac = RAB$C_SEQ;
        if ((!(status & 1)) || (newsitm.itm_grp != tmp->sk_num)) {
          if (!(tmp = tmp->sk_next)) break;
          itm_key[0] = 0;
          itm_key[1] = tmp->sk_num;
          itmrab.rab$b_rac = RAB$C_KEY;
          if (newsgrp.grp_num) {
            if ((newsgrp.grp_count != itm_count) && sys_get_nornf(&grprab)) {
              newsgrp.grp_count = itm_count;
              sys_update(&grprab);
              }
            if (check_id_head) {
              server_check(0,served_cmd);
              itmrab.rab$b_krf = 0;
              itmrab.rab$b_ksz = 8;
              itmrab.rab$l_kbf = (char *) itm_key;
              itmrab.rab$b_rac = RAB$C_KEY;
              itmrab.rab$l_rop= RAB$M_WAT | RAB$M_KGE;

              grprab.rab$l_kbf = (char *) &cur_grp;
              grprab.rab$b_ksz = 4;
              grprab.rab$b_krf = 1;
              grprab.rab$l_rop = RAB$M_WAT;
              grprab.rab$b_rac = RAB$C_KEY;
              }
            }
          newsgrp.grp_num = 0;
          continue;
          }
        }
      if (!newsitm.itm_grp) {
        sys_delete(&itmrab);
        continue;
        }
      if (newsitm.itm_grp != newsgrp.grp_num) {
        if ((newsgrp.grp_num) && (newsgrp.grp_count != itm_count)) {
          if (sys_get_nornf(&grprab)) {
            newsgrp.grp_count = itm_count;
            sys_update(&grprab);
            }
          }

        if (check_id_head) {
          unsigned int sav_contxt[2];

          sav_contxt[0] = newsitm.itm_num;
          sav_contxt[1] = newsitm.itm_grp;
          server_check(0,served_cmd);
          itmrab.rab$b_krf = 0;
          itmrab.rab$b_ksz = 8;
          itmrab.rab$l_kbf = (char *) sav_contxt;
          itmrab.rab$b_rac = RAB$C_KEY;
          itmrab.rab$l_rop = RAB$M_WAT;
          if (!sys_get_nornf(&itmrab)) {
            printf("SKIM: RMS error after server check\n");
            break;
            }
          itmrab.rab$b_rac = RAB$C_SEQ;

          grprab.rab$l_kbf = (char *) &cur_grp;
          grprab.rab$b_ksz = 4;
          grprab.rab$b_krf = 1;
          grprab.rab$l_rop = RAB$M_WAT;
          grprab.rab$b_rac = RAB$C_KEY;
          }
        cur_grp = newsitm.itm_grp;
        grprab.rab$l_rop = RAB$M_WAT | RAB$M_NLK;
        if (sys_get_nornf(&grprab) & 1) {
	  if ((max_parallel_deletes) && (NULL != gdh))
	    start_asynch_directory_delete(dgroup);
	  else {
	    sysprv();
	    while (gdh) {
	      char f[512];
	      sprintf(f,Itm_template,util_dir(dgroup),gdh->di);
	      delete(f);
	      gdt = gdh;
	      gdh = gdh->dn;
	      news_free(gdt);
	      --parallel_files_queued;
	      }
	    nosysprv();
	    }
          if (verbose && *out_line) {
            t=time(0);
            et = t - ot;
            if ( et == 0 ) et = 1;  /* avoid divide by zero */
            printf("\t%s - %d/%d items expired/remaining (%8.8s %ld seconds)\n",
                  out_line,itm_skim_count,itm_count,ctime(&t)+11,et);
            ot = t;
            }
          itm_skim_count = 0;
          strnncpy(out_line,sizeof(out_line),
                   newsgrp.grp_name,util_grp_name_len(newsgrp.grp_name), 0);
          del_all_itms = 0;
          if ((grp_exp_val = newsgrp.grp_itmlife) != 0) {
	    if (grp_exp_val == 65535) grp_exp_time = 0;
	    else grp_exp_time = cur_time - (grp_exp_val * DAY_SECS);
	    }
          else {
            grp_exp_val = glob_exp_val ;
            grp_exp_time = glob_exp_time ;
            }
          }
        else del_all_itms = 1;
        grprab.rab$l_rop = RAB$M_WAT;
        itm_count = 0;
        }

      if ((itm_exp_val = newsitm.itm_life) != 0) {
        if (itm_exp_val == 65535) itm_exp_time = 0;
        else itm_exp_time = cur_time - (itm_exp_val * DAY_SECS);
        }
      else {
        itm_exp_val = grp_exp_val ;
        itm_exp_time = grp_exp_time ;
        }

      if (   del_all_itms
          || (newsitm.itm_recvdate < itm_exp_time)
          || (   (newsgrp.grp_flags & NEWS_M_NNTPSRV)
	      && newsitm.itm_cachedate
	      && (cur_time > (newsitm.itm_cachedate + (newsgrp.grp_srvcache * DAY_SECS))))) {
        if (del_all_itms || (newsitm.itm_recvdate < itm_exp_time)) {
          ++itm_skim_count;
          sys_delete(&itmrab);
          hist_add(newsitm.itm_id);
          }
        else {
          newsitm.itm_cachedate = 0;
          if (served_cmd) {
	    add_check_id(util_grp_name_str(newsgrp.grp_name),
                         newsitm.itm_id,newsgrp.grp_num,
			 newsgrp.grp_srvnode,grp_exp_time,newsgrp.grp_srvproto);
            if (!newsitm.itm_lines) add_recheck(newsitm.itm_id);
            }
          sys_update(&itmrab);
          ++itm_count;
          }
        if (*arch_file) {
                          /* If archiving deleted items, save deletion until
                             after the archive phase */

          dt = (struct dlist *) news_malloc(sizeof *dt);
          dt->dg = news_strdup(newsgrp.grp_name, util_grp_name_len(newsgrp.grp_name));
          dt->di = newsitm.itm_num;
          dt->dn = dh;
          dh = dt;

                        /* If archiving deleted items, then use callable sort
                            (based on the message id) to ensure that only one
                            copy of each item is archived */

          if (!sor_start++) {
	    unsigned short lrl = 512;
	    unsigned int opt = SOR$M_NODUPS;
	    c$cks(sor$begin_sort((unsigned short *) &keys, &lrl, &opt,
				 0,0,0,0,0,0));
            }
          sprintf(itm_fname,Itm_template,util_dir(newsgrp.grp_name),newsitm.itm_num);
          sprintf(sor_line,"%-*.*s %.400s",IDLEN,IDLEN,newsitm.itm_id,itm_fname);
          c$cks(sor$release_rec(c$dsc(sor_line),0));
          }
        else {
	  strnncpy(dgroup,sizeof(dgroup),
                   newsgrp.grp_name,util_grp_name_len(newsgrp.grp_name), 0);
	  sys$setast(0);
	  if (NULL == (gdt = free_gdh))
	    gdt = (struct gdlist *) news_malloc(sizeof *gdt);
	  else
	    free_gdh = gdt->dn;
	  gdt->di = newsitm.itm_num;
	  gdt->dn = gdh;
	  gdh = gdt;
	  ++parallel_files_queued;
	  if (parallel_peak_files_queued < parallel_files_queued)
	    parallel_peak_files_queued = parallel_files_queued;
	  sys$setast(1);
	  }
        continue;
        }

      if (fchk) {
        if (!(fpr = do_oitem(&newsitm,"r",util_grp_name_str(newsgrp.grp_name),0,
			     (newsgrp.grp_flags & NEWS_M_RESTRICT_SET),&flk))) {
          if (flk) _ck_open_info(itm_fname);
          else {
            if (newsgrp.grp_flags & NEWS_M_NNTPSRV) {
              newsitm.itm_cachedate = 0;
              sys_update(&itmrab);
              if (served_cmd)
                add_check_id(util_grp_name_str(newsgrp.grp_name),newsitm.itm_id,
			     newsgrp.grp_num,newsgrp.grp_srvnode,
			     grp_exp_time,newsgrp.grp_srvproto);
              }
            else {
              ++itm_skim_count;
              sys_delete(&itmrab);
              continue;
              }
            }
          }
        else {
          if (fclose(fpr)) _ck_close_info(fpr);
          if (!newsitm.itm_cachedate) {
            time((time_t *) &newsitm.itm_cachedate);
            sys_update(&itmrab);
            }
          }
        }
      ++itm_count;
      }

    if ((max_parallel_deletes) && (NULL != gdh))
      start_asynch_directory_delete(dgroup);
    else {
      sysprv();
      while (gdh) {
	char f[512];
	sprintf(f,Itm_template,util_dir(dgroup),gdh->di);
	delete(f);
	gdt = gdh;
	gdh = gdh->dn;
	news_free(gdt);
	--parallel_files_queued;
	}
      nosysprv();
      }
    while (free_gdh) {
      gdt = free_gdh;
      free_gdh = gdt->dn;
      news_free(gdt);
      }
    while (free_gdl) {
      struct group_dlist *group_dt = free_gdl;
      free_gdl = group_dt->dn;
      news_free(group_dt);
      }
    if (verbose && *out_line) {
      t=time(0);
      et = t - ot;
      if ( et == 0 ) et = 1;  /* avoid divide by zero */
      printf("\t%s - %d/%d items expired/remaining (%8.8s %ld seconds)\n",
                  out_line,itm_skim_count,itm_count,ctime(&t)+11,et);
      ot = t;
      }

    itm_skim_count = 0;
    if (newsgrp.grp_num) {
      if (sys_get_nornf(&grprab)) {
        newsgrp.grp_count = itm_count;
        sys_update(&grprab);
        }
      if (check_id_head) server_check(0,served_cmd);
      }
    }

  if (*arch_file) {
    FILE *fpi, *fpo;
    struct stat app_file, inp_file;
    char xbuf[512];

    if (sor_start) {
      char *p;

      printf("\nSKIM/ARCHIVE - archive pass\n");
      c$cks(sor$sort_merge(0));
      while (sor$return_rec((struct dsc$descriptor *) &a_dsc,
          (unsigned short *) &a_len,0) & 1) {
        sor_line[a_len] = '\0';
        p = strrchr(sor_line,' ');
        p++;
        if (stat(p,&inp_file)) continue;
        if (!(fpi = fopen(p,"r"))) continue;
        if (!stat(arch_file,&app_file)) {
          if ((app_file.st_size + inp_file.st_size) > NEWS_BATCH_SIZE)
            fpo = fopen(arch_file,"w","rfm=var","rat=cr");
          else fpo = fopen(arch_file,"a");
          }
        else fpo = fopen(arch_file,"w","rfm=var","rat=cr");
        if (!fpo) {
          if (fclose(fpi)) _ck_close_info(fpi);
          continue;
          }
        if (fprintf(fpo,"#! rnews %lu\n",inp_file.st_size) < 0) _ck_put_info(fpo);
        while (fgets(xbuf,sizeof(xbuf)-2,fpi)) {
          fputs(xbuf,fpo); _ck_put_info(fpo);
          }
        if (fclose(fpo)) _ck_close_info(fpo);
        if (fclose(fpi)) _ck_close_info(fpi);
        }
      c$cks(sor$end_sort(0));
      }
    else printf("\nSKIM/ARCHIVE - no archive pass (No items to archive)\n");
    while (dh) {
      news_delete_file(dh->di,dh->dg);
      dt = dh;
      dh = dh->dn;
      news_free(dt->dg);
      news_free(dt);
      }
    }

  if (cli$present(c$dsc("NEWSGROUPS")) == CLI$_NEGATED)
    printf("\nSKIM/NONEWSGROUPS - skipping newsgroup scan\n");
  else {
    printf("\nSKIM/NEWSGROUPS - newsgroup scan:\n");
    grp_skim();
    }

  if (!served_cmd) printf("\nSKIM/NOSERVED - skipping remote server scan\n");

  if (cli$present(c$dsc("DIRECTORIES")) == CLI$_NEGATED)
    printf("\nSKIM/NODIRECTORIES - skipping local directory scan\n");
  else {
    printf("\nSKIM/DIRECTORIES - local newsgroup directory scan:\n");
    dir_skim();
    }

  hist_date = (skim_all ? HIST_VAL : 0);
  if ((status = cli$present(c$dsc("HISTORY"))) & 1) {
    cli$get_value(c$dsc("HISTORY"),&arch_dsc,&arch_len);
    arch_file[arch_len] = '\0';
    if (!sscanf(arch_file,"%d",&hist_date)) hist_date = 0;
    }
  else if (status == CLI$_NEGATED) hist_date = 0;
  if (hist_date) {
    int hist_delete_count;

    printf("\nSKIM/HISTORY=%d - history file skim:\n",hist_date);
    time(&cur_time);
    if ( daymod )cur_time -= (cur_time % DAY_SECS);
    hist_date = cur_time - (hist_date * DAY_SECS);
    hist_delete_count = hist_skim(hist_date);
    if (hist_delete_count)
	printf("\t\t- %d records deleted\n", hist_delete_count);
    else
	printf("\t\t- No records deleted\n");
    }

  /*
  ** If any file delete operations are going on in any AST threads, we wait
  ** here for them to complete.  Note:  This should be done BEFORE we start
  ** any /FILECHECK scan since we'd like to keep these operations reasonably
  ** consistent.
  */
  if (0 != parallel_wait_efn) {
    int efns;
    if (SS$_WASCLR == sys$readef(parallel_wait_efn, &efns)) {
      t=time(0);
      printf("\nSKIM/ITEMS=%d - %8.8s waiting for file deletions to complete...",
      	     max_parallel_deletes, ctime(&t)+11);
      sys$waitfr(parallel_wait_efn);
      t=time(0);
      printf("\nSKIM/ITEMS=%d - %8.8s file deletions complete.\n", 
    	     max_parallel_deletes, ctime(&t)+11);
      }
    printf("\nSKIM/ITEMS=%d - Peak background deletion queue: %d files, %d directories.\n", 
	   max_parallel_deletes, parallel_peak_files_queued, parallel_peak_directories_queued);
    parallel_peak_files_queued = parallel_peak_directories_queued = 0;
    lib$free_ef(&parallel_wait_efn);
    parallel_wait_efn = 0;
    }

  if (cli$present(c$dsc("FILES")) == CLI$_NEGATED)
    printf("\nSKIM/NOFILES - skipping local file scan\n");
  else {
    printf("\nSKIM/FILES - local item file scan:\n");
    files_skim();
    }

  clear_gl();
  if (s_smg_active) smg_active = news_context;
  mem_reset(cur_dir_type,1);
  if (mfo) do_open_mail();
  return(0);
}

int
do_skim()
{
  if (nntp_client) return(0);
  return(unwind_display(OUTER_LOOP,doskim));
}

void noserver_add(g)
    unsigned int g;
{
    struct gl_list *tmp;

    tmp = (struct gl_list *) news_malloc(sizeof *tmp);
    tmp->gl_num = g;
    tmp->gl_next = ns_head;
    ns_head = tmp;
}

void noserver_skim()
{
  int status;
  struct gl_list *tmp;
  unsigned int itm_key[2];
  int itmcount,
      delcount = 0,
      flk;
  FILE *fpr;

  for (tmp=ns_head; tmp; ) {
    grprab.rab$l_kbf = (char *) &(tmp->gl_num);
    grprab.rab$b_ksz = 4;
    grprab.rab$b_krf = 1;
    grprab.rab$l_rop = RAB$M_WAT;
    grprab.rab$b_rac = RAB$C_KEY;

    if (sys_get_nornf(&grprab) && (!(newsgrp.grp_flags & NEWS_M_NNTPSRV))) {
      itm_key[0] = 0;
      itm_key[1] = newsgrp.grp_num;
      itmrab.rab$l_kbf = (char *) itm_key;
      itmrab.rab$b_ksz = 8;
      itmrab.rab$b_krf = 0;
      itmrab.rab$l_rop = RAB$M_WAT | RAB$M_KGE;
      itmrab.rab$b_rac = RAB$C_KEY;
      itmcount = 0;
      while (sys_get_nornf(&itmrab)) {
        if (newsitm.itm_grp != newsgrp.grp_num) break;
        itmrab.rab$l_rop = RAB$M_WAT;
        itmrab.rab$b_rac = RAB$C_SEQ;
        if (!(fpr = do_oitem(&newsitm,"r",util_grp_name_str(newsgrp.grp_name),0,
                             (newsgrp.grp_flags & NEWS_M_RESTRICT_SET),&flk))) {
          if (flk) _ck_open_info(itm_fname);
          else {  
            ++delcount;
            sys_delete(&itmrab);
            continue;
            }
          }
        ++itmcount;
        }
      if (itmcount != newsgrp.grp_count) {
        newsgrp.grp_count = itmcount;
        sys_update(&grprab);
        }
      }
    tmp = tmp->gl_next;
    news_free(ns_head);
    ns_head = tmp;
    }
  if (delcount) mem_reset(cur_dir_type,1);
}
#endif
