.title TAIL ;%% ;%% [tmk] VFC support added ;%% [tmk] Ability to look at open files added ;%% [tmk] ACCVIO w/ variable files corrected ;%% [tmk] Fix problem with empty files ;%% [tmk] Fix problem with /OUTPUT qualifier not working ;%% .ident /V1-003/ .subtitle TAIL overall description ; ;******************************************************************************* ;* Title: TAIL * ;* * ;* Version: 1-001 * ;* * ;* Facility: TAIL -- a unix like Tail program. * ;* * ;* Abstract: TAIL is a program to be used to look at the ending lines of a * ;* large files. It works best for LARGE files in the thousand * ;* of blocks range. * ;* * ;* What we do is map a section over the file treat it as if it * ;* were memory. If the file is not of the supported type, an * ;* error message is signaled. * ;* * ;* Environment: This module runs in user mode, no priviliges necessary. * ;* * ;* Author: Peter A. Portante, 1989 * ;******************************************************************************* ; .library /SYS$LIBRARY:LIB.MLB/ $SSDEF $FABDEF $NAMDEF $RMSDEF $SECDEF ; LINE_LEN = 80 BACKUP_BLKS = 5 * 512 ; .psect rms_data, noexe, wrt, pic, noshr, long ; .align long sec_nam: $nam esa = sec_exp_name, - ; Expaned wildcard name ess = NAM$C_MAXRSS, - rsa = sec_ful_name, - ; Full file specification, no rss = NAM$C_MAXRSS ; - wildcards. ; .align long sec_fab: $fab fop = , - ; Name block processing fna = sec_fil_name, - ; File name from command line dna = sec_rel_name, - ; Related file name spec ;%% ;%% [tmk] Ability to look at open files added ;%% fac = , - shr = , - dnm = <.LIS>, - ; Default file name nam = sec_nam, - ; Address of Name Block xab = sec_xabfhc ; List of Extended Attr. Blocks ; .align long sec_xabfhc: $xabfhc ; Set up file header block ; .align long out_nam: $nam esa = out_ful_name, - ; Just use the expanded ess = NAM$C_MAXRSS ; - file name ; .align long out_fab: $fab fop = ,- ; Name block processing fac = , - ; Just write to the file rat = CR, - ; Carriage ret carriage ctrl fna = out_fil_name, - ; File name from command line ;%% ;%% [tmk] Fix problem with /OUTPUT qualifier not working ;%% ;%% dna = out_rel_name, - ; Related file name ;%% dnm = <.LIS>, - ; Default file name nam = out_nam ; Address of Name Block ; .align long out_rab: $rab fab = out_fab, - ; Address of File Access Block mbf = 16, - ; Use 16 buffers for writing rop = ; Write Behind option ; ; ; .psect normal_data, noexe, wrt, pic, noshr ; sec_fil_desc: .long NAM$C_MAXRSS .address sec_fil_name sec_fil_name: .blkb NAM$C_MAXRSS ; Original input file name ; sec_ful_desc: .long NAM$C_MAXRSS .address sec_ful_name sec_ful_name: .blkb NAM$C_MAXRSS ; Full input file name ; sec_exp_desc: .long NAM$C_MAXRSS .address sec_exp_name sec_exp_name: .blkb NAM$C_MAXRSS ; Expanded input file name ; sec_rel_desc: .long NAM$C_MAXRSS .address sec_rel_name sec_rel_name: .blkb NAM$C_MAXRSS ; Related file name ; out_fil_desc: .long NAM$C_MAXRSS .address out_fil_name out_fil_name: .blkb NAM$C_MAXRSS ; Original output file name ; out_ful_desc: .long NAM$C_MAXRSS .address out_ful_name out_ful_name: .blkb NAM$C_MAXRSS ; Full output file name ; ;%% ;%% [tmk] Fix problem with /OUTPUT qualifier not working ;%% ;%%out_rel_name: .blkb NAM$C_MAXRSS ; number_string: .long NAM$C_MAXRSS ; Number string from cmd line .address number_buffer number_buffer: .blkb NAM$C_MAXRSS ; input_parm: .ascid /INPUT/ ; Input file name output_qual: .ascid /OUTPUT/ ; Output file name last_qual: .ascid /LAST/ ; Number of lines to tail ; number: .long 0 ; Number of lines to tail ; p0_space: .long 0 ; Addresses of space allocated p0_space_end: .long 0 ; beg_adr: .long 0 ; Addresses of file mapped end_adr: .long 0 ; already_open: .byte 0 parsed: .byte 0 have_file: .byte 0 printed: .byte 0 ; udf_desc: .ascid /UNDEFINED/ ; Format names fix_desc: .ascid /FIXED/ var_desc: .ascid /VARIABLE/ vfc_desc: .ascid /VARIABLE with FIXED CONTROL/ stm_desc: .ascid /STREAM/ slf_desc: .ascid /STREAM LF/ scr_desc: .ascid /STREAM CR/ ; format_arr: .address udf_desc ; FAB$C_UDF unsupported .address fix_desc ; FAB$C_FIX unsupported .address var_desc ; FAB$C_VAR ;%% ;%% [tmk] VFC support ;%% .address vfc_desc ; FAB$C_VFC .address stm_desc ; FAB$C_STM unsupported .address slf_desc ; FAB$C_STMLF .address scr_desc ; FAB$C_STMCR ; ; ; .psect code, exe, nowrt, pic ; ;******************************************************************************* ;* Tail * ;* * ;* This is the main entry point for the tail procedure. * ;* * ;* Global register use: * ;* * ;* R11 - Section FAB * ;* R10 - Section XAB FHC * ;* R9 - Channel to section file * ;* R8 - Search routine to use * ;* R7 - Scratch * ;* R6 - Scratch * ;* R5 - Scratch * ;* R4 - Scratch * ;* R3 - Scratch * ;* R2 - Scratch * ;******************************************************************************* ; .entry tail, ^m ; clrb already_open ; No output file open clrb parsed ; No parsing yet clrb have_file ; Don't have any files yet clrb printed ; We havn't printed anything ; movab sec_fab, r11 ; Section FAB movab sec_xabfhc, r10 ; Section XAB FHC ; bsbw get_cli ; Get the command line params ; ; loop: bsbw open_input ; Could we get a file? blbs r0, 10$ brb end_loop ; 10$: bsbw open_output ; Open the output file bsbw map_input ; Map file into memory blbc r0, 20$ ; jsb (r8) ; Process the records ; 20$: bsbw close_input ; Close the input file brw loop ; Get another file ; ; end_loop: tstb already_open ; Is there a file open? beql 10$ ; movab out_fab, r2 ; Save output FAB $close fab = (r2) ; Close the output file blbs r0, 10$ ; pushl FAB$L_STV(r2) pushl FAB$L_STS(r2) pushab out_ful_desc pushl #1 pushl #TAIL_CLOSE ; Signal the error calls #5, g^lib$stop ; 10$: movl #1, r0 ; Return status ret ; ; ; ;******************************************************************************* ;* Get_cli * ;* * ;* This routine gets the output file name and the number of lines to tail * ;* off the command line. * ;******************************************************************************* ; get_cli: pushab out_fil_desc ; Where to store return length pushab out_fil_desc ; Where string descriptor is pushab output_qual ; Qualifier to get calls #3, g^cli$get_value ; Get the value off command line blbs r0, get_lines ; pushl r0 pushl #TAIL_NOOUTPUT ; Signal error calls #2, g^lib$stop ; get_lines: pushab number_string ; Where to store return length pushab number_string ; Where string descriptor is pushab last_qual ; Qualifier to get calls #3, g^cli$get_value ; Get the value off command line blbs r0, convert ; pushl r0 pushl #0 pushl #TAIL_NONUMBER ; Signal error calls #3, g^lib$stop ; convert: pushl #3 ; Ignores tabs and blanks pushl #4 ; Want an unsigned longword pushab number ; Where to put converted long pushab number_string ; Where to get convertable long calls #4, g^ots$cvt_tu_l ; Convert the integer blbs r0, chk_zero ; pushl r0 pushab number_string pushl #1 pushl #TAIL_ILLNUMBER ; Signal error calls #4, g^lib$stop ; chk_zero: tstl number bneq done ; pushl #TAIL_NONZERO calls #1, g^lib$stop ; done: rsb ; End Get_cli ; ; ; ;******************************************************************************* ;* Open_input * ;* * ;* This routine opens the input files for processing. Each time it is * ;* called it opens the next file in the list specified on the command line. * ;* When all files have been processed, a 0 is returned in R0. If an error * ;* occurs during the file processing, if it is recoverable, we just signal an * ;* error message and return the next file. If it is not recoverable we signal * ;* an error and then return a 0 in R0. * ;******************************************************************************* ; open_input: tstb have_file ; Do we have a file? bneq have_one ; get_one: mnegb #1, have_file ; Flag, we will have a file now movw #NAM$C_MAXRSS, sec_fil_desc ; Insure maximum length pushab sec_fil_desc ; Where to store return length pushab sec_fil_desc ; Where string descriptor is pushab input_parm ; Qualifier to get calls #3, g^cli$get_value ; Get the value off command line blbs r0, have_one ; clrl r0 ; Leave we are done rsb ; have_one: tstb parsed ; Have we parsed this file name? bneq do_search ; mnegb #1, parsed ; Flag, we will have parsed movb sec_fil_desc, FAB$B_FNS(r11) ; Save the file name length $parse fab = (r11) ; Parse out the file name blbs r0, do_search ; pushl FAB$L_STV(r11) pushl FAB$L_STS(r11) pushab sec_fil_desc pushl #1 pushl #TAIL_PARSE ; Parse failed calls #5, g^lib$signal clrb parsed ; Clear flags clrb have_file brw get_one ; Get another file name ; do_search: movab sec_nam, r0 movb NAM$B_ESL(r0), sec_exp_desc ; Fill expanded descriptor movb NAM$B_ESL(r0), sec_rel_desc ; Fill related descriptor movb NAM$B_ESL(r0), FAB$B_DNS(r11) movzbl NAM$B_ESL(r0), r1 ; Save this for default movc3 r1, @NAM$L_ESA(r0), @FAB$L_DNA(r11) ; $search fab = (r11) ; Search for the file blbs r0, do_open ; clrb parsed ; Clear processing flags clrb have_file ; cmpl r0, #RMS$_NMF ; No more files to search? bneq 20$ brw get_one ; Get another file name ; 20$: pushl FAB$L_STV(r11) pushl FAB$L_STS(r11) pushab sec_exp_desc pushl #1 pushl #TAIL_SEARCH calls #5, g^lib$signal ; Signal search error brw get_one ; Get another file name ; do_open: movab sec_nam, r0 movb NAM$B_RSL(r0), sec_ful_desc ; Fill full descriptor $open fab = (r11) ; Open the file blbs r0, chk_file ; pushl FAB$L_STV(r11) pushl FAB$L_STS(r11) pushab sec_ful_desc pushl #1 pushl #TAIL_OPEN calls #5, g^lib$signal ; Signal open error brw do_search ; Get another file ; chk_file: movzwl FAB$L_STV(r11), r9 ; Save channel of section bsbw check_file ; Check for accepted file types blbs r0, end_open_in ; $dassgn_s - chan = r9 ; Failure, close file blbs r0, 10$ ; pushl r0 pushal sec_ful_desc pushl #1 pushl #TAIL_CLOSE calls #4, g^lib$signal ; Signal if error closing file ; 10$: brw do_search ; Get another file ; end_open_in: rsb ; End of Open_input ; ; ; ;******************************************************************************* ;* Close_input * ;* * ;* This routine closes the input file and deletes the virtual address * ;* space used by it. * ;* * ;* R9 - Channel to the section file * ;******************************************************************************* ; close_input: $deltva_s - ; Delete the virtual address inadr = beg_adr ; - space used by file blbs r0, 10$ ; ;%% ;%% [tmk] Check to see if the error was NO_PRIV. If it was, silently eat it ;%% since it came from a previous error in mapping a null file (can't get a ;%% NO_PRIV on a close otherwise, we'd have seen it on the open as well... ;%% cmpl r0, #SS$_NOPRIV ; No privs? beql 10$ ; Yup, exit quietly pushl r0 pushal sec_ful_desc pushl #1 pushl #TAIL_CLOSE ; Signal the error calls #4, g^lib$signal ; 10$: $dassgn_s - ; Deassign the channel to file chan = r9 blbs r0, 20$ ; pushl r0 pushal sec_ful_desc pushl #1 pushl #TAIL_CLOSE ; Signal the error calls #4, g^lib$signal ; 20$: rsb ; End of Close_input ; ; ; ;******************************************************************************* ;* Open_output * ;* * ;* This routine opens the output file for processing. If the file has * ;* already been open, we just return success. For each file we processed, we * ;* call this routine to make sure the output file is open. If it isn't * ;* already we open it. * ;* * ;* R3 - Output FAB * ;* R2 - Output RAB * ;******************************************************************************* ; open_output: tstb already_open ; Did we already open it? beql 10$ rsb ; 10$: movab out_fab, r3 ; Save output FAB movab out_rab, r2 ; Save output RAB movb out_fil_desc, FAB$B_FNS(r3) ; Save the given file length $parse fab = (r3) ; Parse the given file name blbs r0, 20$ ; pushl FAB$L_STV(r3) pushl FAB$L_STS(r3) pushab out_fil_desc pushl #1 pushl #TAIL_PARSE ; Report the failure and stop! calls #5, g^lib$stop ; 20$: $create fab = (r3) ; Create the output file blbs r0, 30$ ; pushl FAB$L_STV(r3) pushl FAB$L_STS(r3) pushab out_ful_desc pushl #1 pushl #TAIL_OPEN ; Report the failure and stop! calls #5, g^lib$stop ; 30$: $connect rab = (r2) ; Try to connect a record stream blbs r0, 40$ ; pushl RAB$L_STV(r2) pushl RAB$L_STS(r2) pushab out_ful_desc pushl #1 pushl #TAIL_OPEN ; Report the failure and stop! calls #5, g^lib$stop ; 40$: mnegb #1, already_open rsb ; End of Open_output ; ; ; ;******************************************************************************* ;* Check_file * ;* * ;* This routine checks the file's organization and record format to make * ;* sure it is of the supported type. * ;* * ;* R11 - input file FAB * ;* R8 - appropriate search routine to use * ;******************************************************************************* ; check_file: cmpb FAB$B_ORG(R11), - ; Make sure it's sequential file #FAB$C_SEQ beql 10$ ; pushab sec_ful_desc pushl #1 pushl #TAIL_UNSUPORG calls #3, g^lib$signal ; Signal unsupported file org clrl r0 rsb ; 10$: caseb FAB$B_RFM(r11), - ; Check supported formats #FAB$C_UDF, - #FAB$C_MAXRFM - FAB$C_UDF ; rfm_case: .word bad - rfm_case ; FAB$C_UDF unsupported .word bad - rfm_case ; FAB$C_FIX unsupported .word var - rfm_case ; FAB$C_VAR ;%% ;%% [tmk] VFC support ;%% .word vfc - rfm_case ; FAB$C_VFC .word bad - rfm_case ; FAB$C_STM unsupported .word slf - rfm_case ; FAB$C_STMLF .word scr - rfm_case ; FAB$C_STMCR ; bad: movzbl FAB$B_RFM(r11), r1 ; Setup format array indexing movab format_arr, r0 ; pushab sec_ful_desc pushl (r0)[r1] pushl #2 pushl #TAIL_UNSUPRFM calls #4, g^lib$signal ; Unsupported record format clrl r0 ; Report error rsb ; var: movab var_search, r8 ; Variable print routine movl #1, r0 rsb ; ;%% ;%% [tmk] VFC support ;%% vfc: movab vfc_search, r8 ; Variable FC print routine movl #1, r0 rsb ; slf: movab slf_search, r8 ; Stream LF print routine movl #1, r0 rsb ; scr: movab scr_search, r8 ; Stream CR print routine movl #1, r0 rsb ; End of Check_file ; ; ; ;******************************************************************************* ;* Map_input * ;* * ;* This routine maps a given file into memory. * ;******************************************************************************* ; map_input: clrq p0_space ; Clear out return address ;%% ;%% [tmk] Pad the space we ask for as locate_loop was locc'ing off the end of ;%% allocated memory and ACCVIO'ing in some cases... ;%% movl XAB$L_EBK(r10), r0 ; Scratch copy of file size addl2 #4, r0 ; Pad it by 2Kb $expreg_s - pagcnt = r0, - ; Allocate enough memory retadr = p0_space blbs r0, 10$ ; Success? ; pushl r0 pushab sec_ful_desc pushl #1 pushl #TAIL_READ ; Signal the error calls #4, g^lib$signal clrl r0 rsb ; Leave... ; 10$: $crmpsc_s - ; Map the section inadr = p0_space, - retadr = beg_adr, - flags = SEC$M_EXPREG, - chan = r9, - pagcnt = XAB$L_EBK(r10), - pfc = #5 blbs r0, 20$ ; Success? ; ;%% ;%% [tmk] See if the error was end-of-file. If it was, no need to print an ;%% error message (since the tail of an empty file is nothing). ;%% cmpl r0, #SS$_ENDOFFILE ; End of file? beql 5$ ; Yup, exit quietly pushl r0 pushab sec_ful_desc pushl #1 pushl #TAIL_READ ; Signal the error calls #4, g^lib$signal 5$: clrl r0 ; 20$: rsb ; Leave... ; ; ; ;******************************************************************************* ;* Search routines * ;* * ;* These routines page back through the file and located the last N * ;* records then print them out. The routines for STREAM_LF and STREAM_CR are * ;* simple in that they just count backwards the number of LF or CR in file. ** ;* Since VARIABLE records have the length of the record at its beginning we * ;* cannot start from the end of the file and go backwords using defined * ;* markers like in STREAM_LF and STREAM_CR. We ALSO cannot start at the * ;* beginning since it will incurr too many page faults for LARGE files. So * ;* to avoid this, we start looking for the last few records NEAR the end of * ;* the file. TRAPSE is a routine that checks to see if we are on a correct * ;* record byte adding the current word to our address and jumping through the * ;* file until we hit EOF on the nose. * ;* * ;* The routine for VARIABLE records is different, this is the algorithm: * ;* * ;* Take the number of lines to search, multiply by the an * ;* arbitrary line length of LINE_LEN to get number of bytes from * ;* the end of file. This is where we are going to start. If * ;* our starting position is at or before the beginning of the * ;* file, we will just TRAPSE from the beginning of the file and * ;* then print out the lines. If we are not at the beginning of * ;* the file, we will search for a NULL byte. R1 contains the * ;* starting search position and R0 the starting length. R1 is * ;* also saved in R2. * ;* * ;* NO NULL BYTE FOUND: * ;* * ;* Now we must go back further in the file to find one. But we * ;* don't want to search past our current point. So we subract * ;* from our saved position 1536 bytes (3 blocks) and do the search * ;* again. We then resave out current position in R2. * ;* * ;* FOUND: * ;* * ;* If the null byte found is an even address it means it is * ;* either part of a record or the first byte in the WORD record * ;* count. So we will assume that it is the record count and * ;* TRAPSE through the file to see if this accurately brings us * ;* to the end of file. If it doesn't, we will look for the next * ;* null byte in the current section of the file. * ;* * ;* ODD ADDRESS: * ;* * ;* Since the null byte is on an odd address, it could be one of * ;* Three things: * ;* * ;* 1) Second byte in WORD record count * ;* 2) Byte to pad record to even length * ;* 3) Null within record * ;* * ;* First we will check to see if it is the second byte in the * ;* WORD record count and TRAPSE through the file to see if we * ;* get to EOF correctly. If it is not that, we then we use the * ;* following word as a record count and trapse through the file * ;* to see if we get to EOF correctly. If neither of the above * ;* work we go back and find another byte. * ;* * ;* R11 - Section FAB * ;* R10 - Section XAB (File Header Characteristics, FHC) * ;* R9 - Channel to section file * ;* R8 - Print routine to use * ;* R7 - TRAPSE saves the total number of records found here * ;* R6 - TRAPSE saves the address of the first good record it finds * ;* R5 - TRAPSE uses the temporarily for counting the number of records * ;* R4 - Address to start printing * ;* R3 - Address of EOF byte * ;* R2 - Saved start position * ;* R1 - Address of NULL byte, or one byte past end of string * ;* R0 - Current length of data to look for null byte * ;******************************************************************************* ; ;%% ;%% [tmk] VFC support ;%% vfc_search: subl3 #1, XAB$L_EBK(r10), r0 ; Number of blocks - EOF block mull2 #512, r0 ; Calculate number of bytes in movzwl XAB$W_FFB(r10), r2 ; - the file, R2,R0 are scratch addl2 r2, r0 ; Calculate address of byte addl3 beg_adr, r0, r3 ; - after EOF, R2,R0 are scratch ; cmpl r3, beg_adr ; Anything in file? bneq 10$ rsb ; Nothing to do, leave... ; 10$: clrl r5 ; Just for cleanliness movl r3, r6 ; Position to TRAPSE too. clrl r7 ; No records found movab vfc_print, r8 ; Save print routine to use brb varcom ; join common code ; var_search: subl3 #1, XAB$L_EBK(r10), r0 ; Number of blocks - EOF block mull2 #512, r0 ; Calculate number of bytes in movzwl XAB$W_FFB(r10), r2 ; - the file, R2,R0 are scratch addl2 r2, r0 ; Calculate address of byte addl3 beg_adr, r0, r3 ; - after EOF, R2,R0 are scratch ; cmpl r3, beg_adr ; Anything in file? bneq 10$ rsb ; Nothing to do, leave... ; 10$: clrl r5 ; Just for cleanliness movl r3, r6 ; Position to TRAPSE too. clrl r7 ; No records found movab var_print, r8 ; Save print routine to use ;+ ; Now take the number of lines to search, multiply by the an arbitrary line ; length of LINE_LEN to get number of bytes from end of file to start search. ; - ;%% ;%% [tmk] VFC support ;%% varcom: mull3 number, #LINE_LEN, r0 ; Figure distance from EOF subl3 r0, r3, r1 ; Calculate that address movl r1, r2 ; Save our position for later ;+ ; Now see if are starting postion is the beginning of or before the file. ;- cmpl r1, beg_adr ; Are we past the beginning? bgtr locate_loop ; movl beg_adr, r1 ; Address to start print bsbw trapse ; Trapse through file tstl r4 ; No enough records found? bneq 20$ movl beg_adr, r4 ; Print out whole file 20$: bsbw print_records ; Print out the records rsb ; Leave... ;+ ; Now start the search, locate the first null byte from this point ;- locate_loop: locc #0, r0, (r1) ; Find a null byte bneq found_null ; r0 <> 0 we found a null byte ;+ ; Ok, we didn't find a null, now we must go back more in the file to find one. ; But we don't want to search past our current point. So we subract from our ; saved position BACKUP_BLKS and do the search again. ;- subl2 #BACKUP_BLKS, r2 ; Subtract backup blocks cmpl r2, beg_adr ; Past beginning of file? bgtr 20$ ; movl beg_adr, r1 ; Address to start print bsbw trapse ; Trapse through file tstl r4 ; No enough records found? bneq 10$ movl beg_adr, r4 ; Print out whole file 10$: bsbw print_records ; Print out the records rsb ; Leave... ; 20$: movl #BACKUP_BLKS, r0 ; Length to search movl r2, r1 ; Setup address to search brb locate_loop ; Go back in file, search again ;+ ; If the null byte found is an even address it means it is either part of a ; record or the first byte in the WORD record count. So we will assume that ; it is the record count and trapse through the file to see if this accurately ; brings us to the end of file. If it doesn't we will look for the next one. ;- found_null: blbs r1, 10$ ; Is it odd or even address? bsbw trapse ; Look for EOF correctly tstl r4 ; If nothing in R4, failure beql locate_loop ; Go back and search again ; bsbw print_records ; Print out records rsb ; Leave... ;+ ; The null byte is an odd address. So that means it is either one of three ; things: ; 1) Second byte in WORD record count ; 2) Byte to pad record to even length ; 3) Null within record ; First we will check to see if it is the second byte in the WORD record count ; and trapse through the file to see if we get to EOF correctly. If it is not ; that, we then we use the following word as a record count and trapse through ; the file to see if we get to EOF correctly. If neither of the above work ; we go back and find another byte. ;- 10$: movab -1(r1), r1 ; Do first case... bsbw trapse tstl r4 beql 20$ ; bsbw print_records ; Print out records rsb ; Leave... ; 20$: movab 2(r1), r1 ; Do second case... bsbw trapse tstl r4 bneq 30$ ; brw locate_loop ; Third case, get next null byte ; 30$: bsbw print_records ; Print out records rsb ; End of Var_search ; ; ; slf_search: movzbl #10, r5 ; Save terminating byte, LF brb stm_search ; Do the search ; scr_search: movzbl #13, r5 ; Save terminating byte, CR ; stm_search: subl3 #1, XAB$L_EBK(r10), r0 ; Number of blocks - EOF block mull2 #512, r0 ; Calculate number of bytes in movzwl XAB$W_FFB(r10), r2 ; - the file, R2,R0 are scratch addl2 r2, r0 ; Calculate address of byte addl3 beg_adr, r0, r3 ; - after EOF, R2,R0 are scratch clrl r6 ; Clear counter movab stm_print, r8 ; Save print routine to use ; cmpl r3, beg_adr ; Anything in file? bneq 10$ rsb ; Nothing to do, leave... ; 10$: movab -1(r3), r4 ; Save address of last byte cmpl r4, beg_adr ; See if we are not at the end bgtr 20$ rsb ; At the beginning, leave. ; 20$: cmpb -(r4), r5 ; Look for previous stream term. beql 30$ ; cmpl r4, beg_adr ; Don't go past BOF. bgtr 20$ ; bsbw print_records ; Hit beginning of file, print rsb ; Leave... ; 30$: aoblss number, r6, 20$ ; Increment record count ; incl r4 ; We are on the stream term bsbw print_records ; - move to next byte and print. rsb ; End of Stm_search ; ; ; ;******************************************************************************* ;* Trapse * ;* * ;* This routine jumps through the record counts to see if the starting * ;* record count takes us to the EOF byte, while keeping track of how many * ;* records we counted. If we find that the current assumption for a record * ;* takes us to EOF we save it for later testing so we don't keep going all the * ;* way to the EOF on large tail operations. If we are not taken to the EOF * ;* byte, or the last saved valid record, or the number of records is too * ;* small, we just clear R4 and RSB to leave. If we have TOO many records then * ;* we go back through and find the last few we need returning the address of * ;* the records to print out in R4. If the number of records is just we return * ;* the address of the records in R4. If the number of records is too little * ;* we save the valid record in R6 and clear R4. * ;* * ;* R1 - Address to start trapesing through memory * ;* R3 - Address of EOF byte in file * ;* R4 - Address of current memory location * ;* R5 - record count * ;* R6 - Address of first valid record found * ;******************************************************************************* ; trapse: movl r1, r4 ; Save current location clrl r5 ; Clear counter ; trapse_loop: movzwl (r4)+, r0 addl2 r0, r4 ; Move to next record blbc r4, 10$ ; Even address? incl r4 ; No, make it even 10$: incl r5 ; We have another record cmpl r4, r6 ; Compare to EOF blss trapse_loop ; Still less than? ; beql 20$ ; Did we hit EOF? clrl r5 clrl r4 rsb ; Just leave, not good ; 20$: movl r1, r6 ; Save for ending TRAPSE compare addl2 r5, r7 ; Add to total records found cmpl r7, number ; Check against number of lines bgtr shorten ; More than we need, cut down beql success ; Exactly what we want ; clrl r5 ; Clear for cleanliness clrl r4 ; Not enough records rsb ; Leave... ; success: movl r1, r4 ; Records are accurate rsb ; Leave with print address ; shorten: subl2 number, r7 ; Find how many we are over movl r1, r4 ; Start at beginning again 10$: movzwl (r4)+, r0 addl2 r0, r4 ; Move to next record blbc r4, 15$ ; Even address? incl r4 ; No make it even 15$: sobgtr r7, 10$ ; Until we have number we want ; rsb ; End of Trapse ; ; ; ;******************************************************************************* ;* Print routines * ;* * ;* For each of the supported formats, these routines print out the * ;* records from the memory location in R4 on down. * ;* * ;* LATER make characters printable. * ;* * ;* R0 - Scratch * ;* R2 - Address of RAB * ;* R3 - Address of EOF byte * ;* R4 - Address of current memory location * ;* R5 - Byte to locate for STREAM files * ;******************************************************************************* ; print_records: movab out_rab, r2 ; Save our output RAB tstb printed ; Did we print a file before? beql 30$ ; clrw RAB$W_RSZ(r2) ; Blank line bsbw put_record ; Print blank line blbs r0, 10$ ; Did an error occur? rsb ; Leave if one did ; 10$: movzbw sec_ful_desc, RAB$W_RSZ(r2) ; Save file name length movab sec_ful_name, RAB$L_RBF(r2) ; Save file name address bsbw put_record ; Print file name blbs r0, 20$ ; Did an error occur? rsb ; Leave if one did ; 20$: clrw RAB$W_RSZ(r2) ; Blank line bsbw put_record ; Print blank line blbs r0, 30$ ; Did an error occur? rsb ; Leave if one did ; 30$: jsb (r8) ; Print out the records mnegb #1, printed ; We have printed things out rsb ; End of Print_records ; ; ; var_print: movw (r4)+, RAB$W_RSZ(r2) ; Save record length movl r4, RAB$L_RBF(r2) ; Save record address bsbw put_record ; Print the record blbs r0, 10$ ; Test for success brb 30$ ; 10$: movzwl RAB$W_RSZ(r2), r0 ; Restore record length addl2 r0, r4 ; Move past this record blbc r0, 20$ ; Is the record length even? incl r4 ; If not, make it even. 20$: cmpl r4, r3 blss var_print 30$: rsb ; End of Var_print ; ; ; ;%% ;%% [tmk] VFC support ;%% vfc_print: movw (r4)+, RAB$W_RSZ(r2) ; Save record length subw2 #2, RAB$W_RSZ(R2) ; minus the fixed control bytes movl r4, RAB$L_RBF(r2) ; Save record address addl2 #2, RAB$L_RBF(r2) ; past the fixed control bytes bsbw put_record ; Print the record blbs r0, 10$ ; Test for success brb 30$ ; 10$: movzwl RAB$W_RSZ(r2), r0 ; Restore record length addl2 #2, r0 addl2 r0, r4 ; Move past this record blbc r0, 20$ ; Is the record length even? incl r4 ; If not, make it even. 20$: cmpl r4, r3 blss vfc_print 30$: rsb ; End of Vfc_print ; ; ; stm_print: subl3 r4, r3, r0 cmpl #65535, r0 bgeq 10$ ; movzwl #65535, r0 ; 10$: locc r5, r0, (r4) subl3 r4, r1, RAB$W_RSZ(r2) movl r4, RAB$L_RBF(r2) addl3 #1, r1, r4 bsbw put_record blbs r0, 20$ brb 30$ ; 20$: cmpl r4, r3 blss stm_print 30$: rsb ; End of Stm_print ; ; ; ;******************************************************************************* ;* Put_record * ;* * ;* This routine simply calls SYS$PUT to send the record to the output * ;* file. If an error occurs, it is signaled and R0 is cleared. * ;* * ;* R2 - output RAB * ;******************************************************************************* ; put_record: $put rab = (r2) ; Write record to file blbc r0, 10$ rsb ; 10$: pushl RAB$L_STV(r2) pushl RAB$L_STS(r2) pushab out_ful_desc pushl #1 pushl #TAIL_WRITE calls #5, g^lib$signal clrl r0 rsb ; ; ; .end tail