[Search for users] [Overall Top Noters] [List of all Conferences] [Download this site]

Conference noted::hackers_v1

Title:-={ H A C K E R S }=-
Notice:Write locked - see NOTED::HACKERS
Moderator:DIEHRD::MORRIS
Created:Thu Feb 20 1986
Last Modified:Mon Aug 03 1992
Last Successful Update:Fri Jun 06 1997
Number of topics:680
Total number of notes:5456

581.0. "Reading backwards..." by SHIRE::PHILIP (Phil Ward Mgmt. Sci. Geneva-Etang) Thu Oct 15 1987 08:00

		Is there a nice simple way to read backwards
	through a sequential file (fixed or variable-length)?
	
		Phil 
T.RTitleUserPersonal
Name
DateLines
581.1TLE::BRETTThu Oct 15 1987 12:208
    Fixed, yes - just mung the RFA and do the appropriate $GET
    
    Variable, no - I believe there is some support in the Fortran RTL
    (and hence, probably some way of writing a Fortran program to do
    this) but it relies on reading the file forwards first and keeping
    some sort of table.
    
    /Bevin
581.2PYT :== $PYTCXCAD::LARSENGlen LarsenThu Oct 15 1987 17:3114
        I have done this (for kicks) in BLISS and call it PYT for
        no obscure reason.  Someone once mentioned that there is also
        a UNIX-like TAIL utility that will output the last 'n' lines
        of a file.
        
        Well, you are welcome to PYT and can find it at

		        CXCAD::SYS$PUBLIC:PYT.B32, PYT.EXE

        I find it very useful on April Fools day, or when I want to
        look at the bottom of a very large batch log file.  Remember
        that macro just 3 lines from the bottom of STARLET.REQ?

        gl
581.3Sample in BASIC.CASEE::VANDENHEUVELHein, Valbonne.Fri Oct 16 1987 07:15112
	The only reliable /supportable solution to this problem is to
    scan the file forwards using $FIND, build a table of RFA's and then 
    access the records in reverse order using filling in the RFA from
    table and using $GET with RAC=RFA. The storage requirements, and
    even some performance requirements, can be improved by not storing 
    every RFA, but only every some many Kb worth of records, determined
    by the size of the buffer you are willing to use.
    
    	But, I do have this BASIC HACK that actually works on most (all?)
    variable lenght sequntial files. Note, this dates from before the
    time the BASIC supported RABDEF$, so I define RAB and FAB manually
    but that can be removed.
    
    	Have fun,
                 Hein.
    
     1	OPTION TYPE = EXPLICIT	!Hein van den Heuvel, Xmas 1985, Valbonne
	                        On error go to hell !
	EXTERNAL LONG FUNCTION	SYS$OPEN(FAB$TYPE), SYS$CONNECT(RAB$TYPE), &
				SYS$READ(RAB$TYPE), SYS$CLOSE(FAB$TYPE)
	EXTERNAL LONG CONSTANT	RMS$_NORMAL, RMS$_EOF
	DECLARE LONG CONSTANT	FAB_CODE = 20483, RAB_CODE = 17409
	DECLARE LONG CONSTANT	M_GET = 2, M_BIO = 32

	RECORD FAB$TYPE
    	   long START,	long FOP,	long STS,	long STV	&
	  ,long ALQ,	word DEQ,	byte FAC,	byte SHR	&
	  ,long CTX,	byte RTV,	byte ORG,	byte RAT	&
	  ,byte RFM,	long JNL,	long XAB,	long NAM	&
	  ,long FNA,	long DNA,	byte FNS,	byte DNS	&
	  ,word MRS,	long MRN,	word BLS,	byte BKS	&
	  ,byte FSZ,	long DEV,	long SDC,	word GBC	&
	  ,byte ACM,	byte RCF,	long FILL
    	END RECORD

	RECORD RAB$TYPE
	   long START,	long ROP,	long STS,	long STV	&
	  ,long RFA_VBN,word RFA_ID,	word FILL,	long CTX	&
	  ,word FILL,	byte RAC,	byte TMO,	word USZ	&
	  ,word RSZ,	long UBF,	long RBF,	long RHB	&
	  ,long KBF,	byte KSZ,	byte KRF,	byte MBF	&
	  ,byte MBC,	long BKT,	long FAB,	long XAB
	END RECORD

	DECLARE STRING	FILE_NAME, 					&
		LONG	RMS_STATUS, I, THIS_REC, LAST_REC,		&
		WORD	LAST_LEN, LEFT_OVER_LENGHT
	MAP (RMS) RAB$TYPE RAB, FAB$TYPE FAB, STRING NAME_BUFFER = 80
	MAP (BUF) WORD BUF(255), STRING LEFT_OVER = 255
	MAP DYNAMIC (BUF) STRING REC
	INPUT 'File name'; FILE_NAME
	NAME_BUFFER = FILE_NAME
	FAB::START = FAB_CODE			!Set FAB$B_BID and FAB$B_BLN
	RAB::START = RAB_CODE			!Set RAB$B_BID and RAB$B_BLN
	RAB::FAB = LOC(FAB::START)		!Put Address of Fab in Rab
	FAB::FNA = LOC(NAME_BUFFER)		!Put Address of name_buf in Fab
	FAB::FNS = LEN(FILE_NAME)		!Put Lenght of file_name in Fab
	FAB::FAC = M_GET + M_BIO		!READ access in BLOCK I/O mode
	RMS_STATUS = SYS$OPEN(FAB)		!Open the file
    		CALL LIB$STOP(RMS_STATUS BY VALUE) IF RMS_STATUS <> RMS$_NORMAL
    	RMS_STATUS = SYS$CONNECT(RAB)		!Connect a buffer
    		CALL LIB$STOP(RMS_STATUS BY VALUE) IF RMS_STATUS <> RMS$_NORMAL
	RAB::UBF = LOC(BUF(0))			!Put Address of user_buf in Rab
	RAB::USZ = 512%				!Set User buffer Size
 !
 !	Let's go hunt for a place to start by reading the last block and
 !	scanning backwards. I guess using a XABFHC could make this cleaner.
 !
	RAB::BKT = FAB::ALQ			!Stuff the VBN into the RAB
    	RMS_STATUS = SYS$READ(RAB)		!Read bucket in buffer
    	WHILE RMS_STATUS = RMS$_EOF		!Beyond EOF?
	  RAB::BKT = RAB::BKT - 1%		!Go back
    	  RMS_STATUS = SYS$READ(RAB)		!Read bucket in buffer
	NEXT
    	CALL LIB$STOP(RMS_STATUS BY VALUE) IF RMS_STATUS <> RMS$_NORMAL
	LAST_LEN = (RAB::RSZ / 2%) - 1%		!Save starting point
	LAST_REC = RAB::BKT * 512 + RAB::RSZ	!Save starting point
	THIS_REC = LAST_REC			!Init
 !
 !	Got our starting point, now let's go for it!
 !
	WHILE RAB::BKT > 0%			!Loop through the file
    	  RMS_STATUS = SYS$READ(RAB) UNLESS 	!Read (previous) bucket unless&
	    RAB::RFA_VBN = RAB::BKT 		!..already there (eof)
    	  CALL LIB$STOP(RMS_STATUS BY VALUE) UNLESS (RMS_STATUS=RMS$_NORMAL)
	  FOR I = LAST_LEN TO 0 STEP -1		!Loop through block
	    IF LAST_REC = THIS_REC + ((BUF(I) + 1%) AND -2%) THEN !Pointing ok?
	      !		
	      ! Here we point to a word with a value that treated as a record 
	      ! length and added to our current position points to the last
	      ! valid record seen. Chances are pretty slim that this is anything
	      ! other then a *** VALID RECORD ***. Grab it while we can.
	      !
	      REMAP (BUF) STRING FILL=(I+1%)*2%	!Skip the first bit and length&
	        ,REC = BUF(I)			!..and put REC were it should be
	      PRINT REC				!Print the record
	      LAST_REC = THIS_REC - 2%		!New address for last record
	      LAST_LEN = I			!New pointer to last length
	    END IF				!
	    THIS_REC = THIS_REC - 2%		!Address of this (trial) record
	  NEXT I				!Walk the Block
	  LEFT_OVER_LENGHT = LAST_LEN * 2%	!Convert word steps to bytes.
	  REMAP (BUF) REC = LEFT_OVER_LENGHT	!Re-map buffer layout
	  LEFT_OVER = REC			!Save last bit from this block
	  RAB::BKT = RAB::BKT - 1%		!Update the VBN into the RAB
	  LAST_LEN = 255%			!Init pointer for full block
	NEXT					!Loop
	GO TO 2					!All done

 HELL:	PRINT ERT$(ERR) UNLESS ERR = 11
	RESUME 2
 2	END
581.4PASTIS::MONAHANI am not a free number, I am a telephone boxFri Oct 16 1987 08:336
    	How much control do you have over the writing of the file?
    
    	If you can write it storing the RFA of the previous record in
    each subsequent, then you can avoid building the table of RFAs when
    you want to read it. Alternatively, you might be able to write it
    as keyed, with a descending key.
581.5BACKSPACE??GIDDAY::PUCKETTMy karma ran over my dogmaTue Oct 20 1987 04:0115
How does Fortran do BACKSPACE? It can go right back through the file. Is
there a limit on how many records it can go back? (i.e. memory limit on table
as per .3) I saw some code that ran on several systems very slowly, which
read a file backwards essentially:                     -----------

read till EOF, keeping count of records
do 10 i=1,count
   backspace file
   backspace file
   read a record
10 continue

This seemed to work regardless of the file's size.

= Giles =
581.6UFP::MURPHYRick MurphyTue Oct 20 1987 11:254
    RE:.5
    The FORTRAN RTL stores the RFA of the records as you traverse them
    forewards, and uses those RFAs on the backward path.
    	-Rick
581.7PRINT/PAGE=(a,b)VIDEO::OSMANtype video::user$7:[osman]eric.sixWed Oct 21 1987 15:255
It's too bad the output of "PRINT/PAGE=(a,b)" can't be directed to your
own terminal.  That command allows printing just the end of a file if
you know how many pages are in it.

/Eric
581.8Tch! Tch! `Too bad,' indeed! What conference IS this, anyway?MDVAX3::COARMy hero? Vax Headroom, of course!Thu Dec 17 1987 19:2013
    Oh, come on now - what a valid hack!  Write a server symbiont using
    the PSM routines that communicates the information back to you somehow.
    To be a really moby hack, it would identify the job number, search
    on the entire VAXcluster for users signed onto the right username,
    scan their recall buffers for the latest command (you ARE going
    to wait around until your output comes back, aren't you?) to find
    the one that issued the original command (or, more to the point,
    DCL's output buffer), and sends the stuff to you using $BRKTHRU[W], or,
    even hackier, stuffing the text into your terminal's output buffer.
    
    Well???
    
    #ken	:-)}