From:	SMTP%"RELAY-INFO-VAX@CRVAX.SRI.COM"  6-JUL-1993 12:08:48.34
To:	EVERHART
CC:	
Subj:	Re: Logging output of .LOG (VMS TAIL)

From: jan@betelgeuse (Jan K{rrman)
X-Newsgroups: comp.os.vms
Subject: Re: Logging output of .LOG (VMS TAIL)
Date: 6 Jul 1993 13:42:19 GMT
Organization: Uppsala University
Lines: 169
Message-ID: <21bvfr$eo@corax.udac.uu.se>
NNTP-Posting-Host: betelgeuse.tdb.uu.se
X-Newsreader: TIN [version 1.1 PL6]
To: Info-VAX@KL.SRI.COM
X-Gateway-Source-Info: USENET

Rick Flower (flower@donald.etdesg.trw.com) wrote:
: In article <1993Jul6.112503.925@iwsd01.itwol.bhp.com.au> WETIBD@itwol.bhp.com.au (Daiajo Tibdixious) writes:
: >We have a simple tail program here ($ tail/lines={n} {file}), written in
: >macro-11. I think it was written in house by a DEC contractor, so I'll have to
: >check up on its shareablity.
: >
: >Anyone want it posted?
: >
: 
: Yes!!  I've been looking for one of these beasts!
: 
Here is a FORTRAN program that lists the last lines in a sequential file
with variable-length record format or variable-length with fixed-length
control record format. There are some cases which the program may not be
able to handle, read the first comments in the program. The program uses
block I/O to make it efficient for large files.

Compile and link the program, define a symbol by

 tail := $disk:[dir]tail.exe

Now you can e.g list the 12 last lines of a file data.out with the command

 tail data.out,12

Hope this is of any use,
Jan Karrman
Dept. of Scientific Computing
Uppsala University
Sweden
jan@tdb.uu.se

----------- Cut here -------------------------------------------------
c	 This program displays the last lines in a file with variable
c	 length records.
c	 The program may not work if the file contains records longer
c	 then 255 bytes or if the file contains null characters.
c	 Author: Jan K{rrman
c
	include '($rmsdef)'
	include '($fabdef)'
	include '($namdef)'
	include '($rabdef)'
	include '($xabdef)'
	include '($xabfhcdef)'
	structure /x/
	union
	  map
	    record /xabdef/ xab
	  end map
	  map
	    record /xabfhcdef/ xfh
	  end map
	end union
	end structure
	record /x/ x
	record /fabdef/ fab
	record /namdef/ nam
	record /rabdef/ rab
	integer sys$open, sys$connect, sys$read, sys$get, sys$close
	integer file_len, blim1
	logical*1 block(513), bor
	character file*255, line*255

c		Get file name and number of lines to display.
	istat = lib$get_foreign(file,'_File: ',file_len)
	if(.not.istat) call exit(istat)
	ind = index(file,',')
	if( ind .gt. 0 ) then
	  file_len = ind - 1
	  read(file(ind+1:255),*) nlines
	else
	  nlines = 10
	end if

	fab.fab$b_bid = fab$c_bid                   ! Block -
	fab.fab$b_bln = fab$c_bln                   ! identification -
	nam.nam$b_bid = nam$c_bid                   ! and -
	nam.nam$b_bln = nam$c_bln                   ! length -
	rab.rab$b_bid = rab$c_bid                   ! specification -
	rab.rab$b_bln = rab$c_bln                   ! for -
	x.xab.xab$b_cod = xab$c_fhc                 ! various -
	x.xab.xab$b_bln = xab$c_fhclen              ! blocks.
	fab.fab$l_fop = ibset( fab.fab$l_fop, fab$v_nam )    ! Name block used.
	fab.fab$b_fac = ibset( izext(fab.fab$b_fac), fab$v_bro ) ! Bloc&rec I/O
	fab.fab$b_fac = ibset( izext(fab.fab$b_fac), fab$v_get ) ! Read mode. 
	fab.fab$l_nam = %loc(nam)                   ! Loc of name block.
	fab.fab$l_xab = %loc(x.xfh)                 ! Loc of header block.
	fab.fab$l_fna = %loc(file)                  ! Loc of file name string.
	fab.fab$b_fns = file_len                    ! File name length.
	fab.fab$b_shr = fab$m_upi .or. fab$m_shrget .or. fab$m_shrput
	rab.rab$l_fab = %loc(fab)                   ! Loc of file acc block.
	rab.rab$l_ubf = %loc(block)                 ! Loc of block buffer.
	rab.rab$w_usz = 512                         ! Size of block buffer.

	istat = sys$open( fab )                     ! Open file.
	if( .not.istat ) call exit( istat )
	if( fab.fab$b_rfm.ne.2 .and. fab.fab$b_rfm.ne.3) then ! Check record
	  write(*,*) 'Invalid record format'                  ! format, must
	  call exit                                           ! be variable.
	end if
	if( x.xfh.xab$w_lrl .gt. 255 )              ! Long records may not work
	1	write(*,*) 'WARNING: longest record > 255 bytes'
	rab.rab$l_bkt = x.xfh.xab$l_ebk             ! Last block in file.

	istat = sys$connect( rab )                  ! Connect RAB with FAB.
	if( .not.istat ) call exit( istat )
	rab.rab$l_rop = ibset( rab.rab$l_rop, rab$v_bio )  ! Use block I/O.
	istat = sys$read( rab )                     ! Read block
	if( istat.ne.rms$_eof ) then
	  if( .not.istat ) then
	    call exit(istat)
	  else
	    nbyte = x.xfh.xab$w_ffb                 ! Last byte in block.
	  end if
	else
	  rab.rab$l_bkt = rab.rab$l_bkt - 1         ! End of file,
	  istat = sys$read( rab )                   ! try previous block.
	  if( .not.istat ) call exit( istat )
	  nbyte = 512
	end if
c		Read the file backwards, byte for byte, and check for
c		record length specifiers.
	do iline = 1, nlines
	  ichar = 0
	  ind = nbyte
	  bor = .false.
	  do while( .not.bor )
	    ichar = ichar + 1
	    if( ichar .eq. 256 ) then
	      write(*,*)'Record too long'
	      call exit                              ! Too long record! Exit.
	    end if
	    blim1 = jzext(block(ind-1))
	    if( (blim1.eq.ichar-1 .or. blim1.eq.ichar-2) .and.
	1	 block(ind).eq.0 ) bor = .true.      ! Beginning of record?
	    ind = ind - 1
	    if( ind .eq. 1 ) then
	      if( rab.rab$l_bkt .eq. 1 ) goto 100    ! BOF, write entire file.
	      block(513) = block(1)                  ! Beginning of block,
	      rab.rab$l_bkt = rab.rab$l_bkt - 1      ! get previous block,
	      istat = sys$read( rab )                ! Read previous block.
	      if( .not.istat ) call exit( istat )
	      ind = 513
	    end if
	  end do                                     ! Continue until BOR.
	  nbyte = ind-1                              ! We are at BOR,
	end do                                       ! get next record.

100	if( ind .eq. 513 ) then
	  ind = 1
	  rab.rab$l_bkt = rab.rab$l_bkt + 1
	end if
	rab.rab$l_rfa0 = rab.rab$l_bkt               ! Pointers to record used
	rab.rab$w_rfa4 = ind-1                       ! for direct addressing.
	rab.rab$b_rac  = rab$c_rfa                   ! Use direct addressing.
	rab.rab$l_ubf  = %loc(line)                  ! Loc of record buffer.
	rab.rab$w_usz  = 255                         ! Size of record buffer.
	rab.rab$l_rop  = ibclr( rab.rab$l_rop, rab$v_bio )  ! Use record I/O.
	istat = sys$get( rab )                       ! Read 1:st record by RFA.
	rab.rab$b_rac = rab$c_seq                    ! The rest by seq. addr.
	do while( istat.ne.rms$_eof )                ! Read until end of file.
	  if( .not.istat ) call exit( istat )
	  write(*,'(1x,a)') line(1:min0(rab.rab$w_rsz,132)) ! Trunc and write.
	  istat = sys$get( rab )                     ! Read next record.
	end do
	istat = sys$close( fab )                     ! Close file.
	call exit
	end

