/*
**++
**  FACILITY:
**      NEWITMFILE
**
**  ABSTRACT:
**      This is a standalone image which performs a record-by-record copy of
**	the news item indexed file while obeying all RMS interlocks.
**
**	The program attempts to gain exclusive access to the file by taking
**	out the relevant system lock, or failing that by opening up a stop
**	file. This is an attempt to gain exclusive access to the file for the
**	duration of the record copy operation. Even if exclusive access is
**	not obtained, the program will press on and do the copy after waiting
**	for a maximum of 20 minutes for everyone to shut down.
**
**  AUTHOR:
**      Geoff Huston
**
**  COPYRIGHT:
**      Copyright © 1990
**
**  CONTRIBUTIONS:
**	MP	5-Sep-1990
**	Mark Pizzolato - INFO COMM Computer Consulting, Redwood City, Ca
**	PHONE:	(415)369-9366	UUCP:  decwrl!infopiz!mark or uunet!lupine!infopiz!mark
**	DOMAIN:	mark@infocomm.com
**
**	It will halt once the sequential scan halts with an RMS error, while
**	reading the original database, but merely ignores (i.e. discards
**	offending records) records that cause errors while entering into the new
**	database. This process should almost completely salvage an old database
**	that somehow has records that can not be cleaned up (this is the case
**	with the damaged databases that I've seen).
**
**	This process can be VERY slow, but it sure beats rescanning all the
**	newsitem texts, and/or starting the database from scratch.
**	
**	All database changing processing, MUST be stopped while this procedure is
**	being performed.  This includes ADDing of new batches, SKIMming, AND
**	posting by users.  It should be relatively easy to suspend the first two,
**	and I might guess that removing the installed priv for the
**	NEWS_ROOT:NEWS.EXE image might suffice to stop the POSTing of new items.
**	
**	This code is derived from a program quickly written by Geoff Huston.
**	Geoff's original program stopped on any error, giving an useful result
**	file, but, the result ended up loosing any items in the original, and
**	hence acted only as a teaser, giving the impression that you had actually
**	saved something, but depending on where in the original item file the
**	error was (or errors were), and what groups are important to you and/or
**	your users.
**
**	While this program is being run, a line of output is produced whenever
**	item records transition to a new newsgroup, and for every hundred input
**	records.  The contents of bad input records are also displayed as they
**	are encountered and ignored.
**	
**	Mark Pizzolato  (mark@infocomm.com)  26 August 1990.
**      GH
**  	These changes were integrated into the V6.0 program by GH on 5/9/90
**
**	6.1b7	15-Jun-1993	Charles Bailey  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
**	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 09-DEC-1996	Mark Pizzolato	mark@infocomm.com
**	  - minor declaration cleanup to get warningless compile under 
**	    DEC C V5.3 on VAX
**--
*/

#ifdef vaxc
#module NEWITMFILE "V6.1"
#endif

#define _NEWITMFILE_C

/* Cause variable declarations for things declared in NEWSVARIABLES.H */
#define _NEWS_MAIN_PROGRAM

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


int main(void)
{
  int status, incount = 0, outcount = 0, last_grp = 0, grincount = 0;
  char **gn = 0, prgfname[256];
  struct FAB itmfab, itmfab_1, grpfab;
  struct RAB itmrab, itmrab_1, grprab;
  struct XABKEY xabkey_1, xabkey_2;
  struct XABPRO xabpro_1;
  ITM  newsitm;
  GRPF newsgrp;

  if (!((status = acquire_exclusive_lock(20*60)) & 1)) {
    printf("Unable to acquire Exclusive Access to the News System:\n%s\n",
	    strerror(EVMSERR, status));
    printf("Continuing . . .\n");
    }
  itmfab = cc$rms_fab;
  itmfab.fab$b_fac = FAB$M_GET ;
  itmfab.fab$l_fna = (char *) ITM_FILENAME;
  itmfab.fab$b_fns = strlen(itmfab.fab$l_fna);
  itmfab.fab$b_shr = FAB$M_SHRDEL | FAB$M_SHRGET | FAB$M_SHRPUT | FAB$M_SHRUPD;

  itmrab = cc$rms_rab;
  itmrab.rab$l_fab = &itmfab;
  itmrab.rab$l_ubf = (char *) &newsitm;
  itmrab.rab$w_usz = sizeof newsitm;
  itmrab.rab$l_rbf = (char *) &newsitm;
  itmrab.rab$w_rsz = sizeof newsitm;

  if (!((status = sys$open(&itmfab)) & 1)) exit(status);
  if (!((status = sys$connect(&itmrab)) & 1)) exit(status);

  itmfab_1 = cc$rms_fab;
  itmfab_1.fab$l_alq = 3*itmfab.fab$l_alq/4;	/* use 75% of original size */
  itmfab_1.fab$b_bks = 3;
  itmfab_1.fab$w_deq = itmfab.fab$l_alq/8;	/* use 12% extension size */
  itmfab_1.fab$b_fac = FAB$M_PUT ;
  itmfab_1.fab$l_fna = (char *) "sys$scratch:NEWS.ITEMS_WRK";
  itmfab_1.fab$b_fns = strlen(itmfab_1.fab$l_fna);
  itmfab_1.fab$l_fop = FAB$M_CIF | FAB$M_CTG;
  itmfab_1.fab$w_mrs = sizeof newsitm;
  itmfab_1.fab$b_org = FAB$C_IDX;
  itmfab_1.fab$b_rat = FAB$M_CR;
  itmfab_1.fab$b_rfm = FAB$C_FIX;
  itmfab_1.fab$b_shr = FAB$M_SHRDEL | FAB$M_SHRGET | FAB$M_SHRPUT | FAB$M_SHRUPD;
  itmfab_1.fab$l_xab = (char *) &xabkey_1;

  xabkey_1 = cc$rms_xabkey;
  xabkey_1.xab$b_dtp = XAB$C_BN8;
  xabkey_1.xab$b_flg = 0;
  xabkey_1.xab$w_pos0 = (char *) &newsitm.itm_num - (char *) &newsitm;
  xabkey_1.xab$b_ref = 0;
  xabkey_1.xab$b_siz0 = 8;
  xabkey_1.xab$l_nxt = (char *) &xabkey_2;

  xabkey_2 = cc$rms_xabkey;
  xabkey_2.xab$b_dtp = XAB$C_STG;
  xabkey_2.xab$b_flg = 0;
  xabkey_2.xab$w_pos0 = (char *) &newsitm.itm_id - (char *) &newsitm;
  xabkey_2.xab$w_pos1 = (char *) &newsitm.itm_grp - (char *) &newsitm;
  xabkey_2.xab$b_ref = 1;
  xabkey_2.xab$b_siz0 = IDLEN;
  xabkey_2.xab$b_siz1 = 4;
  xabkey_2.xab$l_nxt = (char *) &xabpro_1;

  xabpro_1 = cc$rms_xabpro;
  xabpro_1.xab$w_pro = 0xEE00;

  itmrab_1 = cc$rms_rab;
  itmrab_1.rab$l_fab = &itmfab_1;
  itmrab_1.rab$b_krf = 0;
  itmrab_1.rab$b_ksz = 8;
  itmrab_1.rab$l_ubf = (char *) &newsitm;
  itmrab_1.rab$w_usz = sizeof newsitm;
  itmrab_1.rab$l_rbf = (char *) &newsitm;
  itmrab_1.rab$w_rsz = sizeof newsitm;

  if (!((status = sys$create(&itmfab_1)) & 1)) {
    printf("Cannot create %s with %ld contiguous blocks - using best try\n",
      itmfab_1.fab$l_fna,itmfab_1.fab$l_alq);
    itmfab_1.fab$l_fop &= ~FAB$M_CTG;
    itmfab_1.fab$l_fop |= FAB$M_CBT;
    if (!((status = sys$create(&itmfab_1)) & 1)) exit(status);
    }
  if (!((status = sys$connect(&itmrab_1)) & 1)) exit(status);

  itmrab.rab$l_rop = RAB$M_WAT;
  itmrab.rab$b_rac = RAB$C_SEQ;
  itmrab.rab$b_krf = 0;
  itmrab.rab$b_ksz = 8;

  itmrab_1.rab$l_rop = RAB$M_WAT;
  itmrab_1.rab$b_rac = RAB$C_SEQ;
  itmrab_1.rab$b_krf = 0;
  itmrab_1.rab$b_ksz = 8;

  if (!((status = sys$rewind(&itmrab)) & 1)) exit(status);

  grpfab = cc$rms_fab;
  grpfab.fab$b_bks = 3;
  grpfab.fab$b_fac = FAB$M_GET;
  grpfab.fab$l_fna = (char *) "NEWS_ROOT:NEWS.GROUPS";
  grpfab.fab$b_fns = strlen(grpfab.fab$l_fna);
  grpfab.fab$b_shr = FAB$M_SHRDEL | FAB$M_SHRGET | FAB$M_SHRPUT | FAB$M_SHRUPD;

  grprab = cc$rms_rab;
  grprab.rab$l_fab = &grpfab;
  grprab.rab$l_rbf = grprab.rab$l_ubf = (char *) &newsgrp;
  grprab.rab$w_rsz = grprab.rab$w_usz = NEWS_GRPFIL_RSZ;

  if (sys$open(&grpfab) & 1) {
    if (sys$connect(&grprab) & 1) {
      int key = 0, max_grp, i;
      char tmpstr[132];
  
      grprab.rab$l_kbf = (char *) &key;
      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;
      if (sys$get(&grprab) & 1) {
        gn = (char **) calloc(sizeof(*gn), (max_grp = (newsgrp.grp_topnum + 50)));
        for (i = 0; i < max_grp; ++i) gn[i] = 0;

        grprab.rab$b_krf = 0;
        grprab.rab$b_rac = RAB$C_SEQ;
        _c$cks(sys$rewind(&grprab));
        while (sys$get(&grprab) & 1) {
          strcpy((gn[newsgrp.grp_num] = malloc(strlen(newsgrp.grp_name) + 1)),newsgrp.grp_name);
          }
        for (i=0; i<max_grp; ++i)
	  if (!gn[i])
	    {
	    sprintf(tmpstr, "(Group Number %d, no name)", i);
            strcpy((gn[newsgrp.grp_num] = malloc(strlen(tmpstr) + 1)),tmpstr);
	    }
        }
      }
    _c$cks(sys$close(&grpfab));
    }

  printf("\n");
  for (;;) {
    if ((status = sys$get(&itmrab)) == RMS$_EOF) {
      printf("End of file reached, %d records read, %d records written\n",incount, outcount);
      _c$cks(sys$close(&itmfab));
      _c$cks(sys$close(&itmfab_1));
      printf("Renaming newly created NEWS_ROOT:NEWS.ITEMS_WRK to NEWS.ITEMS");
      rename("sys$scratch:NEWS.ITEMS_WRK", ITM_FILENAME);
      strcpy(prgfname,ITM_FILENAME);
      strcat(prgfname,";-1");
      while (!delete(prgfname));
      exit(1);
      }
    else if (!(status & 1)) {
      printf("ERROR FROM RMS - code returned was %X\n", status);
      printf("Item file copy terminated before Input EOF\n");
      printf("      %d records read, %d records written to NEWS_ROOT:NEWS_ITEMS.WRK\n",incount, outcount);
      exit(status);
      }
    if (!incount) last_grp = newsitm.itm_grp;
    if (incount++ && last_grp != newsitm.itm_grp) {
      if (gn) printf("      Newsgroup: %s, Count: %d\n",gn[last_grp],grincount);
      else printf("      Newsgroup: %d, Count: %d\n",last_grp,grincount);
      grincount = 0;
      last_grp = newsitm.itm_grp;
      }
    if (!(++grincount % 100)) printf("%d\r",grincount);
    if ((status = sys$put(&itmrab_1)) & 1) ++outcount;
    else {
      printf("\n$PUT call error: %X\n",status);
      if (gn) printf("  Item Number:\t\t%d\tNewsgroup:\t%s\n",newsitm.itm_num, gn[newsitm.itm_grp]);
      else printf("  Item Number:\t\t%d\tNewsgroup:\t%d\n",newsitm.itm_num, newsitm.itm_grp);
      printf("  Message ID:\t\t%s\n", newsitm.itm_id);
      printf("  Subject:\t\t%s\n", newsitm.itm_title);
      printf("  Date:\t\t\t%s\tLines:\t\t%d\n",gendate(newsitm.itm_recvdate), newsitm.itm_lines);
      }
    }
}
