[Info-vax] What is your favorite tool lo look at open batch log files? I use a vested ICR LIST from 1988 (binary translated from VAX to Alpha)

Phillip Helbig undress to reply helbig at asclothestro.multivax.de
Sun Nov 8 06:39:50 EST 2020


In article <ro8kgk$csf$1 at dont-email.me>,
=?UTF-8?Q?Jan-Erik_S=c3=b6derholm?= <jan-erik.soderholm at telia.com>
writes: 

> Or, to get a constantly updated display:
> 
> TYPE/CONT/INT=1 <batch-logfile>
> 
> Together with $ SET OUTPUT_RATE=00:00:01 in the batch job.

That is often useful, though TYPE/TAIL sometimes doesn't work (I'm not 
sure exactly when).  Before TYPE/TAIL was available, I used the 
following C code which I got from somewhere (probably comp.os.vms):

#include descrip		/* descriptor details */
#include stdio			/* for printf and stuff */
#include rms			/* for the "real" I/O stuff */
#include dvidef			/* stuff for the GETDVI ss */
#include dcdef			/* device classes */
#include iodef			/* QIO stuff */
#include msgdef			/* mailbox stuff */
#include ssdef			/* completion codes */

struct FAB inp_fab;		/* Input fab/rab/xab */
struct RAB inp_rab;
struct XABFHC inp_xab;

struct FAB out_fab;		/* Output fab/rab */
struct RAB out_rab;

typedef struct {		/* define an item list struct */
   short length;		/* length of buffer */
   short code;			/* item code */
   void *ptr;			/* ptr to buffer */
   void *retlen;		/* ptr to return length */
} Item;

typedef struct {		/* Genernic IOSB struct */
   short status;
   short length;
   long devdepend;
} Iosb;

typedef struct {
   unsigned long block;		/* block # */
   unsigned short offset;	/* offset within block */
   unsigned short length;	/* record length */
} Rfa;

typedef struct {		/* VMS 64 bit quadword time */
   long lsb;
   long msb;
} VMSTime;

unsigned char inp_buf[65536-512]; /* 127 blocks of buffer space */
int the_safe_way=0;		/* If != then read var files front to back */
int monitor;			/* if != then loop on display of tail */
int sec=5;			/* delay time in seconds */
int tti_chan;			/* channel to use to look for stop code */
char tti_text[8];		/* room for terminal input */
Iosb tti_iosb;			/* IOSB for terminal input */
int tti_class;			/* SYS$INPUT device class */
int tto_class;			/* SYS$OUTPUT device class */
int tto_page;			/* SYS$OUTPUT page size (if tto_class == DC$_TERM) */
int were_done;			/* if !=, signals monitor complete */
int last_rfa_blk;		/* saved rfa of last record read */
int last_rfa_off;

char default_string[] = "SYS$DISK:[].LOG"; /* default input filename */

Item tt_dvi[] = {		/* An item list used to get SYS$xxx class */
   {4,DVI$_DEVCLASS,&tto_class,0},
   {4,DVI$_TT_PAGE,&tto_page,0},
   {0,0,0,0}
};

$DESCRIPTOR(sysin,"SYS$INPUT");
$DESCRIPTOR(sysout,"SYS$OUTPUT");

/* rfa stands for "record file address" */

Rfa *rfas;			/* ptr to array of rfa structs */
int record_count=23;		/* number of records to output */
int	maxargs;		/* records size of next_file array */
char **next_file;		/* pts to array of char ptrs to filenames */
VMSTime delay = {-5*10*1000*1000, /* 64 bit VMS delta time format for monitor timer... */
		 -1};		/* ...initialized to 5 seconds */

char *mini_help_msg[] = {
   "TAIL version 2.4, 09/10/91. D. Shepperd, shepperd at dms.UUCP\n",
   "Usage: TAIL [/record_count] [/S] [/F] [/T secs] input_file [output_file]\n",
   0
};

char *help_msg[] = {
   "where: \"[]\" indicates optional data\n",
   " \"/record_count\" is decimal number of records desired\n",
   " \"/S\" indicates to use the \"safer\" mode\n",
   " \"/F\" monitor tail end of file (5 second sample rate)\n",
   " \"/T secs\" monitor tail end of file with sample rate of \"secs\"\n",
   " \"input_file\" is the input filename (can have wildcards)\n",
   " \"output_file\" is output filename (bogus if wildcards on input)\n",
   "Note that a \"-\" can be used in place of the \"/\" to delimit options.\n",
   "White space is required between all arguments (including the /T and secs).\n",
   "Options may appear in any order, but all must preceed filename(s).\n",
   0
};


void mini_help()
{
   char **s;
   for (s=mini_help_msg;*s;++s) fputs(*s,stderr); /* show mini help */
   return;
}

void show_help()
{
   char **s;
   mini_help();
   for (s = help_msg;*s;++s) fputs(*s,stderr);	/* display help message */
   return;
}

int main(int argc,char *argv[])
{
   int param;			/* Parameter counter */
   int err;			/* place to hold rms errors */
   int rfm;			/* loaded with record format code */
   int i, files;

   err = sys$getdviw(0,0,&sysin,&tt_dvi,0,0,0,0);
   if ((err&1) == 0) return err;
   tti_class = tto_class;

   err = sys$getdviw(0,0,&sysout,&tt_dvi,0,0,0,0);
   if ((err&1) == 0) return err;
   if (tto_class == DC$_TERM) record_count = tto_page > 2 ? tto_page-1 : 2;

   inp_fab = cc$rms_fab;	/* init the input fab/rab/xab */
   inp_fab.fab$b_shr = FAB$M_GET|FAB$M_PUT|FAB$M_UPI;
   inp_fab.fab$b_fac = FAB$M_GET|FAB$M_BRO;
   inp_rab = cc$rms_rab;
   inp_rab.rab$l_ubf = inp_buf;
   inp_rab.rab$w_usz = sizeof(inp_buf);	/* assume to read the max */
   inp_xab = cc$rms_xabfhc;
   inp_rab.rab$l_fab = &inp_fab;	/* tell rab where fab is */
   inp_fab.fab$l_xab = &inp_xab;	/* tell fab where xab is */
   inp_fab.fab$l_dna = default_string;	/* say input file defaults */
   inp_fab.fab$b_dns = sizeof(default_string)-1;

   param = 1;			/* start looking at argv[1] */
   --argc;			/* skip the image name */
   while(1) {			/* get all the command options */
      char c,*s;
      if (argc < 1) break;
      s = argv[param];
      c = *s++;
      if (c == '/' || c == '-') {
	 c = *s++;
	 c = toupper(c);
	 switch (c) {
	    int secs;
	    case '?':
	    case 'H':
	       show_help();
	       return 0x10000003;
	    case 'S':		/* safer mode */
	       the_safe_way = 1;
	       break;
	    case 'T':		/* set monitor rate in seconds */
	       if (*s == 0) {
		  if (--argc < 1) {
		     fputs("Missing delay time parameter\n",stderr);
		     mini_help();
		     return 0x10000002;
		  }
		  s = argv[++param];
	       }
	       if (sscanf(s,"%d",&secs) != 1 || secs <= 0 || secs > 2000) {
		  fprintf(stderr,"Invalid delay time parameter: %s\n",s);
		  fputs("Time value must be between 1 and 2000 secs\n",stderr);
		  mini_help();
		  return 0x10000002;
	       }
	       delay.lsb = -secs*10*1000*1000;
	    /* fall through to /F to default to monitor mode if /T specified */
	    case 'F':		/* set monitor mode */
	       monitor = 1;
	       break;
	    default:		/* assume the param is a record count */
	       --s;		/* backup to the first char of the record count */
	       if (sscanf(s,"%d",&record_count) != 1 || record_count <= 0) {
		  fprintf(stderr,"Invalid record count parameter: %s\n",s);
		  mini_help();
		  return 0x10000002;
	       }
 	 }
	 ++param;
	 --argc;
      } else {
	 break;
      }
   }
   record_count++;	/* Fix of wrong record_count. KAR - 6-mar-1990 */
   if (argc < 1) {
      show_help();
      exit(0x10000003);
   }
   files = fgen(argv[param], &next_file, &maxargs);	/* deal with wildcards */
   if (files < 1) {
      fputs("No input file(s) found\n",stderr);
      return 0x10000002;
   }
   if (monitor) {
      if (tti_class != DC$_TERM) {
	 fputs("Cannot monitor files if SYS$INPUT is not a terminal\n",stderr);
	 monitor = 0;
      } else {
	 err = sys$assign(&sysin,&tti_chan,0,0);
	 if ((err&1) == 0) {
	    fputs("Error assigning channel to SYS$INPUT, monitor mode disabled\n",stderr);
	    monitor = 0;
	 }
	 que_ttiread();		/* que up a read to the terminal */
      }
   }
   for (i = 0; i < files; i++) {
      if (files > 1) {
	 if (tto_class == DC$_TERM) {
	    printf("\r\n	 \033[7m**************** %s ****************\033[0m",
		     next_file[i]);
	 } else {
	    printf("\r\n	**************** %s ****************",
		     next_file[i]);
	 }
      }
      inp_fab.fab$l_fna = next_file[i];	/* input filename is next param */
      inp_fab.fab$b_fns = strlen(next_file[i]);
      if (((err=sys$open(&inp_fab))&1) == 0) {
	 fputs("Error opening input file\n",stderr);
	 exit(err);
      }
      if (((err=sys$connect(&inp_rab))&1) == 0) {
	 fputs("Error connecting input rab\n",stderr);
	 exit(err);
      }
      if (inp_fab.fab$b_org != FAB$C_SEQ) {
	 char *oldtype="UNKNOWN";
	 if (inp_fab.fab$b_org == FAB$C_REL) oldtype = "RELATIVE";
	 if (inp_fab.fab$b_org == FAB$C_IDX) oldtype = "INDEXED";
	 if (inp_fab.fab$b_org == FAB$C_HSH) oldtype = "HASHED";
	 fprintf(stderr,"Input file organization is %s. This program only supports SEQUENTIAL.\n",
		  oldtype);
	 exit(0x10000002);
      }
      if (inp_fab.fab$b_rfm == FAB$C_UDF || inp_fab.fab$b_rfm > FAB$C_STMCR) {
	 fputs("Input file has undefined or unsupported record format.\n",stderr);
	 exit(0x10000002);
      }
      out_fab = cc$rms_fab;			/* init the output fab */
      out_fab.fab$b_bks = inp_fab.fab$b_bks;	/* make rest same as input */
      out_fab.fab$w_bls = inp_fab.fab$w_bls;
      out_fab.fab$w_deq = inp_fab.fab$w_deq;
      out_fab.fab$b_fac = FAB$M_PUT;
      out_fab.fab$b_fsz = inp_fab.fab$b_fsz;
      out_fab.fab$w_mrs = inp_fab.fab$w_mrs;
      out_fab.fab$b_rat = inp_fab.fab$b_rat;
      out_fab.fab$b_rfm = inp_fab.fab$b_rfm;
      out_rab = cc$rms_rab;
      out_rab.rab$l_fab = &out_fab;		/* tell rab where fab is */
      if (--argc >= 1) {
	 out_fab.fab$l_fna = argv[++param];	/* output filename is next param */
      } else {
	 out_fab.fab$l_fna = "SYS$OUTPUT:";	/* else default */
      }
      out_fab.fab$b_fns = strlen(out_fab.fab$l_fna);
      if (((err=sys$create(&out_fab))&1) == 0) {
	 fputs("Error opening output file\n",stderr);
	 exit(err);
      }
      if (((err=sys$connect(&out_rab))&1) == 0) {
	 fputs("Error connecting output rab\n",stderr);
	 exit(err);
      }
      rfm = inp_fab.fab$b_rfm;	/* pickup rfm code */
      last_rfa_blk = last_rfa_off = 0;	/* start at top of file */
      were_done = 0;
      while (1) {
	 long oldebk;
	 int oldffb;
	 oldebk = inp_xab.xab$l_ebk;	/* remember the old end of file mark */
	 oldffb = inp_xab.xab$w_ffb;
	 if (last_rfa_blk != 0) {		/* if we've already been through the file */
	    err = skip_through();	/* then just start where we left off */
	 } else {
	    switch (inp_fab.fab$b_rfm) {
	       case FAB$C_FIX:
		  err = do_fixed();
		  break;
	       case FAB$C_VAR:
		  err = do_var();
		  break;
	       case FAB$C_VFC:
		  err = do_var();
		  break;
	       case FAB$C_STM:
		  err = do_stream(2);
		  break;
	       case FAB$C_STMCR:
		  err = do_stream(1);
		  break;
	       case FAB$C_STMLF:
		  err = do_stream(0);
		  break;
	       default:
		  err = 0x10000004;
		  fputs("Unknown record format\n",stderr);
		  exit(err);
	    }
	 }
	 if ((err&1) == 0) {
	    if (err != RMS$_EOF) {
	       fputs("Error reading input\n",stderr);
	       exit(err);
	    }
	 }
	 if (!monitor || were_done == 1) break;
	 while (!were_done) {
	    sys$schdwk(0,0,&delay,0);
	    sys$hiber();
	    err = sys$display(&inp_fab);	/* see if stuff has been added to file */
	    if ((err&1) == 0) {
	       fputs("Error doing $DISPLAY on input, monitor mode cancelled\n",stderr);
	       were_done = 1;
	       break;
	    }
	    if (inp_xab.xab$l_ebk == oldebk && inp_xab.xab$w_ffb == oldffb) {
	       continue;			/* eof hasn't moved, continue waiting */
	    }
   /* This part is goofy. One would think that a simple sys$display would be all that */
   /* should be required, but nooooooo.... DEC has set some internal flags that will  */
   /* not let me read past the old end of file regardless of the fact the the eof has */
   /* moved. What's really goofy is that sys$display notices that the eof has moved   */
   /* but it won't change those internal flags. Closing/reopening the file every few  */
   /* seconds seems like an expensive proposition to me. Grrrr.			      */
	    err = sys$close(&inp_fab);	/* close the file */
	    if ((err&1) == 0) {
	       fputs("Error closing the input file\n",stderr);
	       exit(err);
	    }
	    err = sys$open(&inp_fab);	/* reopen the file */
	    if ((err&1) == 0) {
	       fputs("Error reopening the input file\n",stderr);
	       exit(err);
	    }
	    err = sys$connect(&inp_rab);	/* reconnect the rab */
	    if ((err&1) == 0) {
	       fputs("Error reconnecting the input rab\n",stderr);
	       exit(err);
	    }
	    break;
	 }
      }
      sys$close(&inp_fab);
      sys$close(&out_fab);
   }
   if ((err&1) != 0) return err;
   if (err != RMS$_EOF) return err;
   return SS$_NORMAL;
}

/**************************************************************************
 * do_seqout - sequentially output the data from the input file
 *
 * At entry:
 *	rfa_blk - starting block number
 *	rfa_off - offset within block to start of record
 * At exit:
 *	last_rfa_blk and last_rfa_off are set to the rfa of the last record read
 *	input file has been dumped to output
 **************************************************************************/

do_seqout(long rfa_blk,int rfa_off)	/* seek to desired record and output */
{
   int err,skip=0;
   if (rfa_blk == 0) rfa_blk = 1;	/* start at the beginning */
   if (rfa_blk < last_rfa_blk || (rfa_blk == last_rfa_blk && rfa_off <= last_rfa_off)) {
      rfa_blk = last_rfa_blk;			/* seek to record last displayed */
      rfa_off = last_rfa_off;
      skip = 1;				/* and skip it */
   }
   inp_rab.rab$l_rfa0 = rfa_blk;	/* starting block # */
   inp_rab.rab$w_rfa4 = rfa_off;	/* byte offset within block */
   inp_rab.rab$b_rac = RAB$C_RFA;	/* change to RFA access mode */
   inp_rab.rab$l_bkt = 0;		/* make sure bkt field is clear */
   inp_rab.rab$l_rhb = inp_buf;		/* init the ptrs */
   inp_rab.rab$l_ubf = inp_buf + inp_fab.fab$b_fsz; /* in case making VFC file */
   out_rab.rab$l_rhb = inp_buf;
   out_rab.rab$l_rbf = inp_buf + inp_fab.fab$b_fsz;
   while(1) {
      last_rfa_blk = inp_rab.rab$l_rfa0;	/* save rfa of last record read */
      last_rfa_off = inp_rab.rab$w_rfa4;
      err=sys$get(&inp_rab);		/* read the record */
      if ((err&1) == 0) {
	 if (err != RMS$_EOF) {
	    fputs("Error reading input\n",stderr);
	    exit(err);
	 }
	 break;
      }
      inp_rab.rab$b_rac = RAB$C_SEQ;	/* switch back to sequential reads */
      out_rab.rab$w_rsz = inp_rab.rab$w_rsz;
      if (skip == 0) {			/* if not to skip the record */
	 if (((err=sys$put(&out_rab))&1) == 0) { /* write it */
	    fputs("Error writing output\n",stderr);
	    exit(err);
	 }
      }
      skip = 0;				/* at most, we skip 1 record */
   }
   return err;
}

char end_mark[] = "\n\r\n";

/**************************************************************************
 * do_stream - figure out the record structure for one of the 3 types of
 *		stream files there are.
 * At entry:
 *	type - record type. 0=stmlf, 1=stmcr, 2=stmcrlf
 * At exit:
 *	has called do_seqout with the computed block and offset of the
 *	desired starting record.
 **************************************************************************/

do_stream(type)
int type;	/* 0=stmlf, 1=stmcr, 2=stmcrlf */
{
   unsigned long block,rfa_blk=0;
   int rcd_num= 0,err,rfa_off,part1=0,end_char;
   unsigned char *s;

   inp_rab.rab$l_bkt = inp_xab.xab$l_ebk+1;
   end_char = end_mark[type];

   while(1) {				/* as long as there's data in the file */
      if (inp_rab.rab$l_bkt <= 1) {	/* quit if we already read blk 1 */
	 rfa_blk = 1;			/* give 'em the whole file */
	 rfa_off = 0;
	 break;
      }
      block = inp_rab.rab$l_bkt-(inp_rab.rab$w_usz>>9);
      if (block == 0 || block > inp_rab.rab$l_bkt) block = 1;	/* but can't start before 1 */
      inp_rab.rab$l_bkt = block;	/* rememebr starting block number */
      err = sys$read(&inp_rab);
      if ((err&1) == 0) {
	 if (err == RMS$_EOF) break;
	 fprintf(stderr,"Error (%08X) trying to read %d bytes at block %d\n",
		err,inp_rab.rab$w_usz,block);
	 continue;
      }
      s = inp_rab.rab$l_ubf+inp_rab.rab$w_rsz;

      if (part1) {
	 if (*--s == '\r') {
	    rfa_blk = block+((s+2)-inp_rab.rab$l_ubf>>9);
	    rfa_off = ((s+2)-inp_rab.rab$l_ubf)&511;
	    if (++rcd_num >= record_count) break;
	 }
	 ++s;
      }
      part1 = 0;
      while (s >= inp_rab.rab$l_ubf) {
	 if (*--s == end_char) {
	    char *nrp;
	    nrp = s+1;			/* next record starts here */
	    if (type == 2) {		/* if strmcrlf */
	       if (s <= inp_rab.rab$l_ubf) {	/* if on the cusp */
		  part1 = 1;		/* defer */
		  break;		/* get somemore data */
	       }
	       if (*--s != '\r') {	/* else if next char is not cr */
		  ++s;			/* then this is not a record */
		  continue;
	       }
	    }
	    rfa_blk = block+(nrp-inp_rab.rab$l_ubf>>9);
	    rfa_off = (nrp-inp_rab.rab$l_ubf)&511;
	    if (++rcd_num >= record_count) break;
	 }	
      }
      if (rcd_num >= record_count) break;	/* we got everything */
   }
   if (rfa_blk == 0) return RMS$_EOF;
   return do_seqout(rfa_blk,rfa_off);	/* write out records */
}

/************************************************************************
 * do_varfast. This procedure trys to figure out the record structure
 * of a variable length file while reading from the end of the file.
 *
 * At entry:
 *	(called by do_var)
 * At exit:
 *	returns a 0 if it couldn't determine a valid record structure.
 *	called do_seqout with a computed block and offset if it did
 *		successfuly find an appropriate record boundary.
 *	returns with RMS status in all cases.
 *************************************************************************/

do_varfast()
{
   unsigned long block,rfa_blk=0;
   int rcd_num= 0,err,rec_len,rfa_off,min_recsiz;
   union {
      unsigned char *s;
      unsigned short *len;
      unsigned int align;
   } rp;

   inp_rab.rab$l_bkt = inp_xab.xab$l_ebk+1;
   rec_len = 0;
   min_recsiz = inp_fab.fab$b_fsz;
   inp_rab.rab$w_usz = ((inp_xab.xab$w_lrl+3)*record_count+511)&~511;

   while(1) {
      if (inp_rab.rab$l_bkt <= 1) break; /* quit if we already read blk 1 */
      block = inp_rab.rab$l_bkt-(inp_rab.rab$w_usz>>9);
      if (block == 0 || block > inp_rab.rab$l_bkt) block = 1;	/* but can't start before 1 */
      inp_rab.rab$l_bkt = block;	/* remember starting block number */
      err = sys$read(&inp_rab);
      if ((err&1) == 0) {
	 if (err == RMS$_EOF) break;
	 fprintf(stderr,"Error (%08X) trying to read %d bytes at block %d, record %d\n",
		err,inp_rab.rab$w_usz,block,rcd_num);
	 continue;
      }
      rp.s = inp_rab.rab$l_ubf+inp_rab.rab$w_rsz;
      if ((rp.align&1) == 1) ++rp.align;	/* ffb must be even */

/*************************************************************************
 * The following procedure loops through the buffer picking up pairs of bytes (a
 * short) on even byte boundaries from the end. If this pair of bytes forms a
 * integer whose value points within 1 of the the next record or the current eof,
 * then the pair is assumed to be the count field of a valid record and is so
 * recorded. It can easily get screwed up if there is binary data in the records,
 * but for ascii files such as log files, it ought to work well enough most of
 * the time. The integer value represents the length of the record. If this value
 * is greater than the maxium record length recorded for the file (in the
 * XAB$W_LRL field of the XABFHC), then this routine assumes to have gotten lost,
 * so it rolls over and dies.
 *************************************************************************/

      while (1) {
	 int len,t;
	 char *tp;
	 rp.s -= 2;			/* backup 2 bytes */
	 if (rp.s < inp_rab.rab$l_ubf) break;	/* too far, get more data */
	 len = *rp.len;
	 if (rec_len >= min_recsiz &&	/* if currently at or greater than min */
	     (len == rec_len ||		/* and lengths match exactly */
	      (rec_len > 0 &&		/* or less by 1 */
	       len == rec_len-1))) {
	    rfa_blk = block+(rp.s-inp_rab.rab$l_ubf>>9); /* remember this point */
	    rfa_off = (rp.s-inp_rab.rab$l_ubf)&511;
	    rec_len = 0;
	    if (++rcd_num >= record_count) break;
	    continue;
	 }
	 rec_len += 2;			/* increase size of record */
	 if (rec_len > inp_xab.xab$w_lrl) return 0; /* we're lost, give up */
      }
      if (rcd_num >= record_count) break;	/* we got everything */
   }
   if (rfa_blk == 0) return RMS$_EOF;
   return do_seqout(rfa_blk,rfa_off);	/* write out records */
}

/************************************************************************
 * skip_through - this routine positions to the desired record begining
 * with a known starting position. It is used in monitor mode to skip
 * records that may have been added to the input file since the last
 * time it was looked at it.
 *
 * At entry:
 *	last_rfa_blk and last_rfa_off point to the last record read.
 * At exit:
 *	called do_seqout with a computed block and offset
 *	last_rfa_blk and last_rfa_off point to a new last record read.
 *
 ************************************************************************/

int skip_through()
{
   int err;
   int first_rfa=0;		/* index to first rfa */
   int next_rfa=0;		/* index to next available rfa */

   if (rfas == (Rfa *)0) rfas = (Rfa *)calloc(record_count,sizeof(Rfa));
   inp_rab.rab$l_rfa0 = last_rfa_blk;	/* where we left off */
   inp_rab.rab$w_rfa4 = last_rfa_off;
   inp_rab.rab$b_rac = RAB$C_RFA;	/* change to RFA access mode */
   inp_rab.rab$l_bkt = 0;		/* make sure bkt field is off */
   inp_rab.rab$l_rhb = inp_buf;		/* init the ptrs */
   inp_rab.rab$l_ubf = inp_buf + inp_fab.fab$b_fsz; /* in case making VFC file */
   while (1) {
      (rfas+next_rfa)->block = inp_rab.rab$l_rfa0;
      (rfas+next_rfa)->offset = inp_rab.rab$w_rfa4;
      next_rfa += 1;
      next_rfa %= record_count;
      if (next_rfa == first_rfa) {
	 first_rfa += 1;
	 first_rfa %= record_count;
      }
      err=sys$get(&inp_rab);
      if ((err&1) == 0) {
	 if (err != RMS$_EOF) {
	    fputs("Error reading input\n",stderr);
	    exit(err);
	 }
	 break;
      }
      inp_rab.rab$b_rac = RAB$C_SEQ;	/* switch back to sequential */
   }
   return do_seqout((rfas+first_rfa)->block,(rfas+first_rfa)->offset);
}

/************************************************************************
 * do_var. This procedure positions to the nth record from the end of
 * a file by reading the whole file into memory 127 blocks at a time
 * and skipping through the records recording the block and offset of
 * each as it goes.
 *
 * At entry:
 *
 * At exit:
 *	called do_seqout with the computed block and offset
 *	last_rfa_blk and last_rfa_off updated to point to the last record read.
 *	returns with RMS status.
 *************************************************************************/

do_var()
{
   long block,rcd_num= 0;
   int err;
   int first_rfa=0;		/* index to first rfa */
   int next_rfa=0;		/* index to next available rfa */
   unsigned char *ebp;
   union {
      char *s;
      unsigned short *len;
      unsigned int align;
   } rp;

/* Unless otherwise indicated, trys to do the fast way first. If that fails,
 * it'll do the hard way. */

   if (the_safe_way == 0 && inp_xab.xab$l_ebk > (inp_rab.rab$w_usz>>9)) {
      if ((err=do_varfast()) != 0) return err;
      fputs("Unable to determine record structure from backend of file.\n",stderr);
      fputs("Doing it the 'safer' way instead.\n",stderr);
      inp_rab.rab$w_usz = sizeof(inp_buf);	/* assume to read the max */
   }	
   rp.s = inp_rab.rab$l_ubf;
   inp_rab.rab$l_bkt = 1;
   if (rfas == (Rfa *)0) rfas = (Rfa *)calloc(record_count,sizeof(Rfa));
   while(1) {
      block = inp_rab.rab$l_bkt;
      err = sys$read(&inp_rab);
      inp_rab.rab$l_bkt += (inp_rab.rab$w_rsz+511) >> 9;
      if ((err&1) == 0) {
	 if (err == RMS$_EOF) break;
	 fprintf(stderr,"Error (%08X) trying to read %d bytes at block %d, record %d\n",
		err,inp_rab.rab$w_usz,block,rcd_num);
	 continue;
      }
      ebp = inp_rab.rab$l_ubf+inp_rab.rab$w_rsz;
      while (1) {
	 int len,t;
	 char *tp;
	 if ((rp.align&1) == 1) ++rp.align;
	 if (rp.s >= ebp) {
	    t = rp.s - ebp;
	    inp_rab.rab$l_bkt += t>>9;
	    rp.s = inp_rab.rab$l_ubf+(t&511);
	    break;
	 }
	 ++rcd_num;
	 len = *rp.len;
	 if (len > 32767) {
	    if (len == 0xFFFF) {
	       rp.s = inp_rab.rab$l_ubf;
	       break;			/* eof */
	    }
	    fprintf(stderr,"Warning: Record %d (blk=0x%08X,off=0x%04X): cnt %04X greater than 0x7FFF\n",
			rcd_num,(rp.s-inp_rab.rab$l_ubf>>9)+block,(rp.s-inp_rab.rab$l_ubf)&511,len);
	 }
	 t = rp.s-inp_rab.rab$l_ubf;
	 (rfas+next_rfa)->block = block + (t >> 9);
	 (rfas+next_rfa)->offset = t & 511;
	 (rfas+next_rfa)->length = len;
	 next_rfa += 1;
	 next_rfa %= record_count;
	 if (next_rfa == first_rfa) {
	    first_rfa += 1;
	    first_rfa %= record_count;
	 }
	 rp.s += len+2;
      }
   }
   if ((rfas+first_rfa)->block == 0) return RMS$_EOF;
   return do_seqout((rfas+first_rfa)->block,(rfas+first_rfa)->offset);
}

/************************************************************************
 * do_fixed. This procedure positions to the nth record from the end of
 * a fixed length record file by computing the desired rfa from the
 * required record number, the size of the file and the size of each
 * record. This is by far the simplest of the three decoding routines.
 *
 * At entry:
 *
 * At exit:
 *	called do_seqout with the computed block and offset
 *	last_rfa_blk and last_rfa_off updated to point to the last record read.
 *	returns with RMS status.
 *************************************************************************/

do_fixed()
{
   int reccnt;
   unsigned long size,sbn;
   long start,rfa_blk,rfa_off;

   size = inp_xab.xab$l_ebk*512+inp_xab.xab$w_ffb; /* file size in bytes */
   reccnt = size/inp_xab.xab$w_mrz;	/* total record count */
   start = reccnt - record_count;	/* starting record # */
   if (start < 0) start = 0;		/* can't go past beginning */
   sbn = start*inp_xab.xab$w_mrz;	/* starting byte # */
   rfa_blk = (sbn >> 9) + 1;		/* starting block number */
   rfa_off = sbn & 511;			/* offset in block */
   if (rfa_blk < last_rfa_blk || (rfa_blk == last_rfa_blk && rfa_off < last_rfa_off)) {
      rfa_blk = last_rfa_blk;		/* don't display records already displayed */
      rfa_off = last_rfa_off+inp_xab.xab$w_mrz; /* advance 1 record */
      if (rfa_off >= 512) {		/* and adjust pointers if appropriate */
	 rfa_off -= 512;
	 rfa_blk += 1;
      }
   }
   return do_seqout(rfa_blk,rfa_off);	/* write the end of the file */
}

int tti_ast()
{
   int sts;
   if (tti_iosb.status == SS$_ABORT) {
      return SS$_NORMAL;		/* aborts are ok, since they'll happen at exit */
   }
   if (tti_iosb.status == SS$_NORMAL || tti_iosb.status == SS$_ENDOFFILE) {
      were_done = 1;			/* signal that we're done */
      sys$canwak(0,0);			/* cancel our schwk */
      sys$wake(0,0);			/* wake up from our hiber */
   } else {
      if ((tti_iosb.status&1) == 0) {
	 fputs("Error reading from maillbox\n",stderr);
	 exit(tti_iosb.status);
      }
   }
   return que_ttiread();
}

int que_ttiread()
{
   int sts;
   sts = sys$qio(	0,		/* efn */
			tti_chan,	/* channel */
			IO$_READVBLK,	/* function */
			&tti_iosb,	/* iosb addr */
			tti_ast,		/* astadr */
			0,		/* astparam */
			tti_text,	/* p1 (buffer ptr) */
			sizeof(tti_text), /* p2 (buffer size) */
			0,0,0,0);	/* p3-p6 not used */

   if ((sts&1) == 0) {
      fputs("unable to do QIO to SYS$INPUT, monitor mode disabled\n",stderr);
      monitor = 0;
   }
   return sts;
}

/* ==============================fgen.c============================== */

#include <descrip.h>
/* #include <rms.h>  ...defined in the program top... */

/*
 * fgen(pattern, result_array, array_length)
 *   fgen generates filenames of files, matching a VMS pattern,
 *   the results are stored in an array, space for the strings is allocated
 *   by using malloc.
 * Return values:
 *   -1	   : error in pattern
 *    0	   : no files found
 *   n(>0) : number of matching filenames. (Stored in result_array [0] - [n-1])
 *
 * Wildcard expansion for VMS is easy;	we just use a run-time library call.
 */

fgen(pat,resarry,len)
char *pat,**resarry[];
int *len;
{
    struct dsc$descriptor_s file_spec, result, deflt;
    long context;
    int count, slen, status, plen;
    char *pp, *rp, result_string[256], *strchr();
    char *fnp,**fnpp;

    file_spec.dsc$w_length  = strlen(pat);
    file_spec.dsc$b_dtype   = DSC$K_DTYPE_T;
    file_spec.dsc$b_class   = DSC$K_CLASS_S;
    file_spec.dsc$a_pointer = pat;

    result.dsc$w_length	 = sizeof result_string;
    result.dsc$b_dtype	 = DSC$K_DTYPE_T;
    result.dsc$b_class	 = DSC$K_CLASS_S;
    result.dsc$a_pointer = result_string;

    deflt.dsc$w_length	= sizeof(default_string)-1;
    deflt.dsc$b_dtype	= DSC$K_DTYPE_T;
    deflt.dsc$b_class	= DSC$K_CLASS_S;
    deflt.dsc$a_pointer = default_string;

    count = 0;
    context = 0;
    pp = strrchr(pat, ']');
    if ( !pp ) pp = strrchr(pat, ':');
    if ( !pp ) plen = 0;
    else plen = pp - pat + 1;
    fnpp = *resarry;
    while ((status = LIB$FIND_FILE(&file_spec, &result, &context, &deflt))
							      == RMS$_NORMAL) {
	if (count >= *len) {
	    if (*len == 0) *len = 256;
	    if (*resarry == (char **)0) {
		*resarry = (char **)malloc(*len*sizeof(char *));
	    } else {
		*len += *len/2;		/* increase length by 1/2 again */
		*resarry = (char **)realloc(*resarry,*len*sizeof(char *));
	    }
	    if (*resarry == (char **)0) {
		perror("Unable to malloc/realloc memory");
		exit(0x10000004);
	    }
	    fnpp = *resarry + count;
	}
	rp = strrchr(result_string, ']') + 1;
	if( !rp )
		rp = result_string;
	slen = strchr(rp, ' ') - rp;
	    fnp = *fnpp++ = malloc(slen + plen + 1);
	if (plen != 0)
	    strncpy(fnp, pat, plen);
	strncpy(fnp + plen, rp, slen);
	fnp[slen + plen] = '\0';
	++count;
    }
#ifdef DVI$_ALT_HOST_TYPE
    lib$find_file_end(&context);    /* Only on V4 and later */
#endif
    if (status == RMS$_FNF) return(0);
    if (status == RMS$_NMF) return(count);
    return(-1);
}




More information about the Info-vax mailing list