TAIL.BCKdTAIL.BCKKBACK *.* CMKRNL::[ANONYMOUS.SAVE_SETS]TAIL.BCK/NOCRC/GROUP:0/BLOCK=8192/SAV AWPSYS `"@lV5.1 _VAXB::  V5.0 (*[DCOLIB.TS_SUPPORTED.TAIL]MAKEFILE.MAK;8+,h ./ 4 -20123KPWO56`I97y@89@7AG HJLINK_QUAL=/notrace tail.exe : tail.obj tail_msg.obj link $(LINK_QUAL) $,?tail.obj : tail.martail_msg.obj : tail_msg.msg$*[DCOLIB.TS_SUPPORTED.TAIL]TAIL.CLD;6+, l./ 420-20123KPWO5697@lz@89@7AG HJdefine verb TAIL image tal:TAIL/ parameter P1, label = INPUT, prompt = "File", value (required, list, type = $infile) qualifier LAST default* value (default = 20, type = $number) qualifier OUTPUT default2 value (default = "SYS$OUTPUT", type = $file)%*[DCOLIB.TS_SUPPORTED.TAIL]TAIL.EXE;21+, u./ 4-20123 KPWO56 .@7 z@89@7AG HJ0DX0205(w@TAILV1-001 w@05-02    ?! LIBRTL_001$+1 8 INPUTE OUTPUTS LAST w UNDEFINED FIXED VARIABLE VARIABLE with FIXED CONTROL STREAM  STREAM LF  STREAM CRo `P@ ,`+P@ $DH <`^,n䐏P;/ݬ^|VX^,n䐏Qݬf^PRݬl$լ ݬf  RP|VD^,nȐSȞo؞cܕlլݬfЬ̑lլ  lլ,n䞭l լ Ь l լЬlլ ݬf^<`^,n䐏Tl լЬ^<`^,n䐏Uxݬb^id_ZR[Z0S0P00Ph031'.RbPݢ ݢݏ: PPPݏT zPPݏ\ }Iq PP7ݏd[ P ݏlF P/GPPE4k0P*ݫ ݫݏ  1kP d c 5 Q(Q 0kHP6Pʂ1:ݫ ݫݏ` 1PkPݫ ݫݏ' 1_< Y0P'Xj`h4PARSE$error parsing file specification !AS.SEARCHerror searching for file !AS&OPENerror opening file !AS(CREATEerror creating file !AS*WRITEerror writing to file !AS*READerror reading from file !AS&CLOSEerror closing file !AS>UNSUPORG*file organization of !AS is not sequential>UNSUPRFM+!AS record format not supported in file !AS,NOOUTPUTno output file specified0NONUMBERno number of lines speicified: ILLNUMBER&Illegal decimal number specified "!AS"@NONZERO-number of lines must be greater than zero (0)TAIL@@ht`HP  @LIBRTL$*[DCOLIB.TS_SUPPORTED.TAIL]TAIL.HLP;3+, . / 4G j-20123KPWO 56 +@7z@89@7AG HJ1 TAILF Displays the tail end of a file or group of files on the current output device. Format: TYPE file-spec[,...] 2 Parameters file-spec[,...]F Specifies one or more files to be displayed. If you specify a fileF name and do not specify a file type, the TAIL command uses the default file type LIS.F If you specify two or more files, separate the file specificationsF with either commas or plus signs. The files are displayed in the order listed.F You can specify wildcard characters in place of the directory name,F file name, file type, or file version number field. The TAIL? command displays all files that satisfy the file description.8 DECnet and magnetic tape operations are not supported.2 Command_Qualifiers/OUTPUT /OUTPUT[=file-spec] /NOOUTPUTF Controls where the output of the command is sent. If you do notF enter the qualifier, or if you enter /OUTPUT without a fileF specification, the output is sent to the current process defaultE output stream or device, identified by the logical name SYS$OUTPUT.F If you enter /OUTPUT with a partial file specification (for example,F /OUTPUT=[JONES]), TYPE is the default file name and LIS the defaultF file type. If you enter a file specification, it may not include any wildcard characters./ If you enter /NOOUTPUT, output is suppressed.A The /OUTPUT qualifier is incompatible with the /PAGE qualifier./LAST /LAST[=20]F Requests that the last twenty (20) lines of the file be displayed.F The default number to print is twenty. Any number greater than zero my be used. 2 Examples 1. $ TAIL COMMON.DATF The TAIL command requests that the last twenty lines of the file* COMMON.DAT be displayed at the terminal. 2. $ TAIL *.DATE This is the first of the last twenty lines in the file AA.DAT. . . . ^OE This is the first of the last twenty lines in the file BB.DAT. . . .  Interrupt $ STOPF The TAIL command contains a wildcard character in place of the fileF name. All files with file types of DAT are scheduled for display.F When CTRL/O is pressed, output of the current file stops and theF TAIL command begins displaying the next file. CTRL/Y interrupts the8 command; the STOP command terminates the TAIL command.# 3. $ TAIL AA.DAT, BB.DAT, *.MEME This is the first of the last twenty lines in the file AA.DAT. . . . SYS$DISK:[HOME]BB.DAT;1E This is the first of the last twenty lines in the file BB.DAT. . . . SYS$DISK:[HOME]AA.MEM;1E This is the first of the last twenty lines in the file AA.MEM. . . . SYS$DISK:[HOME]BB.MEM;1E This is the first of the last twenty lines in the file BB.MEM. . . .F The TAIL command displays the last twenty lines of the files on theF command line. The contents of each file is divided by the name of the file to be printed next.* 4. $ TAIL NOTICE.TEXT/OUTPUT=TEMP.TEXTG The TAIL command requests that the last twenty lines of the fileG NOTICE.TEXT be written to the output file TEMP.TEXT rather than to SYS$OUTPUT. 5. $ TAIL THIS.DAT/LAST=30G The TAIL command requests that the last thirty lines of the file5 THIS.DAT be displayed on the current output device.%*[DCOLIB.TS_SUPPORTED.TAIL]TAIL.MAR;51+, /.]/ 4Q]] -20123KPWO^56 t@7 +{@89@7AG HJ .title TAIL .ident /V1-001/2 .subtitle TAIL overall description;P;*******************************************************************************P;* Title: TAIL *P;* *P;* Version: 1-001 *P;*  TAIL.BCK /2%[DCOLIB.TS_SUPPORTED.TAIL]TAIL.MAR;51Q] *P;* Facility: TAIL -- a unix like Tail program. *P;* *P;* Abstract: TAIL is a program to be used to look at the ending lines of a *P;* large files. It works best for LARGE files in the thousand *P;* of blocks range. *P;* *P;* What we do is map a section over the file treat it as if it *P;* were memory. If the file is not of the supported type, an *P;* error message is signaled. *P;* *P;* Environment: This module runs in user mode, no priviliges necessary. *P;* *P;* Author: Peter A. Portante, 1989 *P;*******************************************************************************;5 .library /SYS$LIBRARY:LIB.MLB/ $SSDEF $FABDEF $NAMDEF $RMSDEF $SECDEF; LINE_LEN = 80BACKUP_BLKS = 5 * 512;> .psect rms_data, noexe, wrt, pic, noshr, long; .align longGsec_nam: $nam esa = sec_exp_name, - ; Expaned wildcard name- ess = NAM$C_MAXRSS, -M rsa = sec_ful_name, - ; Full file specification, no> rss = NAM$C_MAXRSS ; - wildcards.; .align longGsec_fab: $fab fop = , - ; Name block processingM fna = sec_fil_name, - ; File name from command lineH dna = sec_rel_name, - ; Related file name spec+ fac = , -& shr = , -C dnm = <.LIS>, - ; Default file nameG nam = sec_nam, - ; Address of Name BlockO xab = sec_xabfhc ; List of Extended Attr. Blocks; .align longJsec_xabfhc: $xabfhc ; Set up file header block; .align longGout_nam: $nam esa = out_ful_name, - ; Just use the expanded= ess = NAM$C_MAXRSS ; - file name; .align longGout_fab: $fab fop = ,- ; Name block processingH fac = , - ; Just write to the fileL rat = CR, - ; Carriage ret carriage ctrlM fna = out_fil_name, - ; File name from command lineC dna = out_rel_name, - ; Related file nameC dnm = <.LIS>, - ; Default file nameG nam = out_nam ; Address of Name Block; .align longNout_rab: $rab fab = out_fab, - ; Address of File Access BlockL mbf = 16, - ; Use 16 buffers for writingE rop = ; Write Behind option;;;; .psect normal_data, noexe, wrt, pic, noshr;$sec_fil_desc: .long NAM$C_MAXRSS% .address sec_fil_nameJsec_fil_name: .blkb NAM$C_MAXRSS ; Original input file name;$sec_ful_desc: .long NAM$C_MAXRSS% .address sec_ful_nameFsec_ful_name: .blkb NAM$C_MAXRSS ; Full input file name;$sec_exp_desc: .long NAM$C_MAXRSS% .address sec_exp_nameJsec_exp_name: .blkb NAM$C_MAXRSS ; Expanded input file name;$sec_rel_desc: .long NAM$C_MAXRSS% .address sec_rel_nameCsec_rel_name: .blkb NAM$C_MAXRSS ; Related file name;$out_fil_desc: .long NAM$C_MAXRSS% .address out_fil_nameKout_fil_name: .blkb NAM$C_MAXRSS ; Original output file name;$out_ful_desc: .long NAM$C_MAXRSS% .address out_ful_nameGout_ful_name: .blkb NAM$C_MAXRSS ; Full output file name;$out_rel_name: .blkb NAM$C_MAXRSS;Mnumber_string: .long NAM$C_MAXRSS ; Number string from cmd line& .address number_buffer$number_buffer: .blkb NAM$C_MAXRSS;Ainput_parm: .ascid /INPUT/ ; Input file nameBoutput_qual: .ascid /OUTPUT/ ; Output file nameIlast_qual: .ascid /LAST/ ; Number of lines to tail;Inumber: .long 0 ; Number of lines to tail;Np0_space: .long 0 ; Addresses of space allocatedp0_space_end: .long 0;Jbeg_adr: .long 0 ; Addresses of file mappedend_adr: .long 0;already_open: .byte 0parsed: .byte 0have_file: .byte 0printed: .byte 0;>udf_desc: .ascid /UNDEFINED/ ; Format namesfix_desc: .ascid /FIXED/"var_desc: .ascid /VARIABLE/5vfc_desc: .ascid /VARIABLE with FIXED CONTROL/ stm_desc: .ascid /STREAM/#slf_desc: .ascid /STREAM LF/#scr_desc: .ascid /STREAM CR/;Gformat_arr: .address udf_desc ; FAB$C_UDF unsupportedG .address fix_desc ; FAB$C_FIX unsupported; .address var_desc ; FAB$C_VARG .address vfc_desc ; FAB$C_VFC unsupportedG .address stm_desc ; FAB$C_STM unsupported= .address slf_desc ; FAB$C_STMLF= .address scr_desc ; FAB$C_STMCR;;;- .psect code, exe, nowrt, pic;P;*******************************************************************************P;* Tail *P;* *P;* This is the main entry point for the tail procedure. *P;* *P;* Global register use: *P;* *P;* R11 - Section FAB *P;* R10 - Section XAB FHC *P;* R9 - Channel to section file *P;* R8 - Search routine to use *P;* R7 - Scratch *P;* R6 - Scratch *P;* R5 - Scratch *P;* R4 - Scratch *P;* R3 - Scratch *P;* R2 - Scratch *P;*******************************************************************************;A .entry tail, ^m;E clrb already_open ; No output file open@ clrb parsed ; No parsing yetJ clrb have_file ; Don't have any files yetL clrb printed ; We havn't printed anything;= movab sec_fab, r11 ; Section FABA movab sec_xabfhc, r10 ; Section XAB FHC;M bsbw get_cli ; Get the command line params;;Floop: bsbw open_input ; Could we get a file? blbs r0, 10$ brb end_loop;F10$: bsbw open_output ; Open the output fileF bsbw map_input ; Map file into memory blbc r0, 20$;E jsb (r8) ; Process the records;F20$: bsbw close_input ; Close the input fileB brw loop ; Get another file;;Gend_loop: tstb already_open ; Is there a file open? beql 10$;A movab out_fab, r2 ; Save output FABG $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 #1B pushl #TAIL_CLOSE ; Signal the error& calls #5, g^lib$stop;?10$: movl #1, r0 ; Return status ret;;;P;*******************************************************************************P;* Get_cli *P;* *P;* This routine gets the output file name and the number of lines to tail *P;* off the command line. *P;*******************************************************************************;Nget_cli: pushab out_fil_desc ; Where to store return lengthL pushab out_fil_desc ; Where string descriptor isB pushab output_qual ; Qualifier to getP 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;Nget_lines: pushab number_string ; Where to store return lengthL pushab number_string ; Where string descriptor isB pushab last_qual ; Qualifier to getP 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;Iconvert: pushl #3 ; Ignores tabs and blanksK pushl #4 ; Want an unsigned longwordM pushab number ; Where to put converted longO pushab number_string ; Where to get convertable longE 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;;;P;*******************************************************************************P;* Open_input *P;* *P;* This routine opens the input files for processing. Each time it is *P;* called it opens the next file in the list specified on the command line. *P;* When all files have been processed, a 0 is returned in R0. If an error *P;* occurs during the file processing, if it is recoverable, we just signal an *P;* error message and return the next file. If it is not recoverable we signal *P;* an error and then return a 0 in R0. *P;*******************************************************************************;Dopen_input: tstb have_file ; Do we have a file? bneq have_one;Oget_one: mnegb #1, have_file ; Flag, we will have a file nowK movw #NAM$C_MAXRSS, sec_fil_desc ; Insure maximum lengthN pushab sec_fil_desc ; Where to store return lengthL pushab sec_fil_desc ; Where string descriptor isB pushab input_parm ; Qualifier to getP calls #3, g^cli$get_value ; Get the value off command line$ blbs r0, have_one;C clrl r0 ; Leave we are done rsb;Phave_one: tstb parsed ; Have we parsed this file name?! bneq do_search;K mnegb #1, parsed ; Flag, we will have parsedP movb sec_fil_desc, FAB$B_FNS(r11) ; Save the file name lengthI $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_fileG brw get_one ; Get another file name;#do_search: movab sec_nam, r0N movb NAM$B_ESL(r0), sec_exp_desc ; Fill expanded descriptorM movb NAM$B_ESL(r0), sec_rel_desc ; Fill related descriptor5 movb NAM$B_ESL(r0), FAB$B_DNS(r11)G movzbl NAM$B_ESL(r0), r1 ; Save this for default; movc3 r1, @NAM$L_ESA(r0), @FAB$L_DNA(r11);E $search fab = (r11) ; Search for the file# blbs r0, do_open;H clrb parsed ; Clear processing flags! clrb have_file;J cmpl r0, #RMS$_NMF ; No more files to search? bneq 20$G 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_SEARCHE calls #5, g^lib$signal ; Signal search errorG brw get_one ; Get another file name;#do_open: movab sec_nam, r0J movb NAM$B_RSL(r0), TAIL.BCK /2%[DCOLIB.TS_SUPPORTED.TAIL]TAIL.MAR;51Q]_ 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_OPENC calls #5, g^lib$signal ; Signal open errorB brw do_search ; Get another file;Ichk_file: movzwl FAB$L_STV(r11), r9 ; Save channel of sectionO bsbw check_file ; Check for accepted file typesi' blbs r0, end_open_in*;* $dassgn_s -*E chan = r9 ; Failure, close file  blbs r0, 10$ ;  pushl r0$ pushal sec_ful_desc pushl #1# pushl #TAIL_CLOSE N calls #4, g^lib$signal ; Signal if error closing file; B10$: brw do_search ; Get another file; Cend_open_in: rsb ; End of Open_input ; ; ; P;*******************************************************************************P;* Close_input *P;* *P;* This routine closes the input file and deletes the virtual address *P;* space used by it. *P;* *P;* R9 - Channel to the section file *P;*******************************************************************************; Lclose_input: $deltva_s - ; Delete the virtual addressF inadr = beg_adr ; - space used by file blbs r0, 10$*;* pushl r0$ pushal sec_ful_desc pushl #1B pushl #TAIL_CLOSE ; Signal the error( calls #4, g^lib$signal;AN10$: $dassgn_s - ; Deassign the channel to file! chan = r9 blbs r0, 20$E;N pushl r0$ pushal sec_ful_desc pushl #1B pushl #TAIL_CLOSE ; Signal the error( calls #4, g^lib$signal;D20$: rsb ; End of Close_input;s; ;_P;*******************************************************************************P;* Open_output *P;* *P;* This routine opens the output file for processing. If the file has *P;* already been open, we just return success. For each file we processed, we *P;* call this routine to make sure the output file is open. If it isn't *P;* already we open it. *P;* *P;* R3 - Output FAB *P;* R2 - Output RAB *P;*******************************************************************************; Iopen_output: tstb already_open ; Did we already open it?  beql 10$  rsbM;EA10$: movab out_fab, r3 ; Save output FABPA movab out_rab, r2 ; Save output RABaP movb out_fil_desc, FAB$B_FNS(r3) ; Save the given file lengthK $parse fab = (r3) ; Parse the given file name  blbs r0, 20$e;m% pushl FAB$L_STV(r3)>% pushl FAB$L_STS(r3) $ pushab out_fil_desc pushl #1N pushl #TAIL_PARSE ; Report the failure and stop!& calls #5, g^lib$stop; H20$: $create fab = (r3) ; Create the output file blbs r0, 30$ ; % pushl FAB$L_STV(r3)% pushl FAB$L_STS(r3)n$ pushab out_ful_desc pushl #1N pushl #TAIL_OPEN ; Report the failure and stop!& calls #5, g^lib$stop;_P30$: $connect rab = (r2) ; Try to connect a record stream blbs r0, 40$ ; % pushl RAB$L_STV(r2)e% pushl RAB$L_STS(r2) $ pushab out_ful_desc pushl #1N pushl #TAIL_OPEN ; Report the failure and stop!& calls #5, g^lib$stop;_(40$: mnegb #1, already_openD rsb ; End of Open_output; ; ; P;*******************************************************************************P;* Check_file *P;* *P;* This routine checks the file's organization and record format to make *P;* sure it is of the supported type. *P;* *P;* R11 - input file FAB *P;* R8 - appropriate search routine to use *P;*******************************************************************************; Pcheck_file: cmpb FAB$B_ORG(R11), - ; Make sure it's sequential file" #FAB$C_SEQ beql 10$ ;n$ pushab sec_ful_desc pushl #1& pushl #TAIL_UNSUPORGM calls #3, g^lib$signal ; Signal unsupported file org  clrl r0 rsbN; I10$: caseb FAB$B_RFM(r11), - ; Check supported formats % #FAB$C_UDF, -a1 #FAB$C_MAXRFM - FAB$C_UDF ;aGrfm_case: .word bad - rfm_case ; FAB$C_UDF unsupportedcG .word bad - rfm_case ; FAB$C_FIX unsupportedA; .word var - rfm_case ; FAB$C_VAR G .word bad - rfm_case ; FAB$C_VFC unsupported G .word bad - rfm_case ; FAB$C_STM unsupportedA= .word slf - rfm_case ; FAB$C_STMLF = .word scr - rfm_case ; FAB$C_STMCRs; Mbad: movzbl FAB$B_RFM(r11), r1 ; Setup format array indexingA& movab format_arr, r0;e$ pushab sec_ful_desc pushl (r0)[r1] pushl #2& pushl #TAIL_UNSUPRFMK calls #4, g^lib$signal ; Unsupported record format > clrl r0 ; Report error rsbe;yHvar: movab var_search, r8 ; Variable print routine movl #1, r0 rsb ;*Islf: movab slf_search, r8 ; Stream LF print routine  movl  TAIL.BCK /2%[DCOLIB.TS_SUPPORTED.TAIL]TAIL.MAR;51Q]E/ #1, r0 rsb ; Iscr: movab scr_search, r8 ; Stream CR print routine  movl #1, r0C rsb ; End of Check_file ; ; ; P;*******************************************************************************P;* Map_input *P;* *P;* This routine maps a given file into memory. *P;*******************************************************************************;*Jmap_input: clrq p0_space ; Clear out return address $expreg_s - K pagcnt = XAB$L_EBK(r10), - ; Allocate enough memory ) retadr = p0_space : blbs r0, 10$ ; Success?;* pushl r0$ pushab sec_ful_desc pushl #1B pushl #TAIL_READ ; Signal the error( calls #4, g^lib$signal clrl r0: rsb ; Leave...; A10$: $crmpsc_s - ; Map the section + inadr = p0_space, -+ retadr = beg_adr, - / flags = SEC$M_EXPREG, -f$ chan = r9, -2 pagcnt = XAB$L_EBK(r10), - pfc = #5: blbs r0, 20$ ; Success?;g pushl r0$ pushab sec_ful_desc pushl #1B pushl #TAIL_READ ; Signal the error( calls #4, g^lib$signal clrl r0; :20$: rsb ; Leave...; ; ; P;*******************************************************************************P;* Search routines *P;* *P;* These routines page back through the file and located the last N *P;* records then print them out. The routines for STREAM_LF and STREAM_CR are *Q;* simple in that they just count backwards the number of LF or CR in file. ** P;* Since VARIABLE records have the length of the record at its beginning we *P;* cannot start from the end of the file and go backwords using defined *P;* markers like in STREAM_LF and STREAM_CR. We ALSO cannot start at the *P;* beginning since it will incurr too many page faults for LARGE files. So *P;* to avoid this, we start looking for the last few records NEAR the end of *P;* the file. TRAPSE is a routine that checks to see if we are on a correct *P;* record byte adding the current word to our address and jumping through the *P;* file until we hit EOF on the nose. *P;* *P;* The routine for VARIABLE records is different, this is the algorithm: *P;* *P;* Take the number of lines to search, multiply by the an *P;* arbitrary line length of LINE_LEN to get number of bytes from *P;* the end of file. This is where we are going to start. If *P;* our starting position is at or before the beginning of the *P;* file, we will just TRAPSE from the beginning of the file and *P;* then print out the lines. If we are not at the beginning of *P;* the file, we will search for a NULL byte. R1 contains the *P;* starting search position and R0 the starting length. R1 is *P;* also saved in R2. *P;* *P;* NO NULL BYTE FOUND: *P;* *P;* Now we must go back further in the file to find one. But we *P;* don't want to search past our current point. So we subract *P;* from our saved position 1536 bytes (3 blocks) and do the search *P;* again. We then resave out current position in R2. *P;* *P;* FOUND: *P;* *P;* If the null byte found is an even address it means it is *P;* either part of a record or the first byte in the WORD record *P;* count. So we will assume that it is the record count and *P;* TRAPSE through the file to see if this accurately brings us *P;* to the end of file. If it doesn't, we will look for the next *P;* null byte in the current section of the file. *P;* *P;* ODD ADDRESS: *P;* *P;* Since the null byte is on an odd address, it could be one of *P;* Three things: *P;* *P;* 1) Second byte in WORD record count *P;* 2) Byte to pad record to even length *P;* 3) Null within record *P;* *P;* First we will check to see if it is the second byte in the *P;* WORD record count and TRAPSE through the file to see if we *P;* get to EOF correctly. If it is not that, we then we use the *P;* following word as a record count and trapse through the file *P;* to see if we get to EOF correctly. If neither of the above *P;* work we go back and find another byte. *P;* *P;* R11 - Section FAB *P;* R10 - Section XAB (File Header Characteristics, FHC) *P;* R9 - Channel to section file *P;* R8 - Print routine to use *P;* R7 - TRAPSE saves the total number of records found here *P;* R6 - TRAPSE saves the address of the first good record it finds *P;* R5 - TRAPSE uses the temporarily for counting the number of records *P;* R4 - Address to start printing *P;* R3 - Address of EOF byte *P;* R2 - Saved start position *P;* R1 - Address of NULL byte, or one byte past end of string *P;* R TAIL.BCK /2%[DCOLIB.TS_SUPPORTED.TAIL]TAIL.MAR;51Q],>0 - Current length of data to look for null byte *P;*******************************************************************************;1Nvar_search: subl3 #1, XAB$L_EBK(r10), r0 ; Number of blocks - EOF blockN mull2 #512, r0 ; Calculate number of bytes inO movzwl XAB$W_FFB(r10), r2 ; - the file, R2,R0 are scratch K addl2 r2, r0 ; Calculate address of byteoP addl3 beg_adr, r0, r3 ; - after EOF, R2,R0 are scratch; C cmpl r3, beg_adr ; Anything in file?  bneq 10$ I rsb ; Nothing to do, leave... ; F10$: clrl r5 ; Just for cleanlinessI movl r3, r6 ; Position to TRAPSE too.eB clrl r7 ; No records foundK movab var_print, r8 ; Save print routine to usei;+K; Now take the number of lines to search, multiply by the an arbitrary linelM; length of LINE_LEN to get number of bytes from end of file to start search.(; -J mull3 number, #LINE_LEN, r0 ; Figure distance from EOFH subl3 r0, r3, r1 ; Calculate that addressM movl r1, r2 ; Save our position for laterr;+I; Now see if are starting postion is the beginning of or before the file.A;-L cmpl r1, beg_adr ; Are we past the beginning?# bgtr locate_loopf; H movl beg_adr, r1 ; Address to start printE bsbw trapse ; Trapse through fileoJ tstl r4 ; No enough records found? bneq 20$uF movl beg_adr, r4 ; Print out whole fileG20$: bsbw print_records ; Print out the recordsn: rsb ; Leave...;+B; Now start the search, locate the first null byte from this point;-Blocate_loop: locc #0, r0, (r1) ; Find a null byteN bneq found_null ; r0 <> 0 we found a null byte;+N; Ok, we didn't find a null, now we must go back more in the file to find one.M; But we don't want to search past our current point. So we subract from ouru5; saved position BACKUP_BLKS and do the search again.u;-H subl2 #BACKUP_BLKS, r2 ; Subtract backup blocksI cmpl r2, beg_adr ; Past beginning of file? bgtr 20$n;lH movl beg_adr, r1 ; Address to start printE bsbw trapse ; Trapse through file:J tstl r4 ; No enough records found? bneq 10$ F movl beg_adr, r4 ; Print out whole fileG10$: bsbw print_records ; Print out the records : rsb ; Leave...; B20$: movl #BACKUP_BLKS, r0 ; Length to searchI movl r2, r1 ; Setup address to searchiO brb locate_loop ; Go back in file, search again ;+K; If the null byte found is an even address it means it is either part of ahL; record or the first byte in the WORD record count. So we will assume thatN; it is the record count and trapse through the file to see if this accuratelyM; brings us to the end of file. If it doesn't we will look for the next one.*;-Lfound_null: blbs r1, 10$ ; Is it odd or even address?H bsbw trapse ; Look for EOF correctlyK tstl r4 ; If nothing in R4, failureoJ beql locate_loop ; Go back and search again;nC bsbw print_records ; Print out records : rsb ; Leave...;+K; 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 N; First we will check to see if it is the second byte in the WORD record countN; and trapse through the file to see if we get to EOF correctly. If it is notN; that, we then we use the following word as a record count and trapse throughK; the file to see if we get to EOF correctly. If neither of the above work #; we go back and find another byte. ;-B10$: movab -1(r1), r1 ; Do first case... bsbw trapse tstl r4 beql 20$A;_C bsbw print_records ; Print out recordsr: rsb ; Leave...;lC20$: movab 2(r1), r1 ; Do second case...  bsbw trapse tstl r4 bneq 30$ ;1P brw locate_loop ; Third case, get next null byte; C30$: bsbw print_records ; Print out records C rsb ; End of Var_search0;0;;Kslf_search: movzbl #10, r5 ; Save terminating byte, LFn? brb stm_search ; Do the search1; Kscr_search: movzbl #13, r5 ; Save terminating byte, CR; Nstm_search: subl3 #1, XAB$L_EBK(r10), r0 ; Number of blocks - EOF blockN mull2 #512, r0 ; Calculate number of bytes inO movzwl XAB$W_FFB(r10), r2 ; - the file, R2,R0 are scratch K addl2 r2, r0 ; Calculate address of byte P addl3 beg_adr, r0, r3 ; - after EOF, R2,R0 are scratch? clrl r6 ; Clear counterpK movab stm_print, r8 ; Save print routine to use;C cmpl r3, beg_adr ; Anything in file?* bneq 10$ I rsb ; Nothing to do, leave... ; K10$: movab -1(r3), r4 ; Save address of last byte N cmpl r4, beg_adr ; See if we are not at the end bgtr 20$.J rsb ; At the beginning, leave.; P20$: cmpb -(r4), r5 ; Look for previous stream term. beql 30$ ; D cmpl r4, beg_adr ; Don't go past BOF. bgtr 20$*;*N bsbw print_records ; Hit beginning of file, print: rsb ; Leave...;sH30$: aoblss number, r6, 20$ ; Increment record count;0K incl r4 ; We are on the stream term P bsbw print_records ; - move to next byte and print.C rsb ; End of Stm_search ; ; ;NP;*******************************************************************************P;* Trapse *P;* *P;* This routine jumps through the record counts to se TAIL.BCK /2%[DCOLIB.TS_SUPPORTED.TAIL]TAIL.MAR;51Q]Me if the starting *P;* record count takes us to the EOF byte, while keeping track of how many *P;* records we counted. If we find that the current assumption for a record *P;* takes us to EOF we save it for later testing so we don't keep going all the *P;* way to the EOF on large tail operations. If we are not taken to the EOF *P;* byte, or the last saved valid record, or the number of records is too *P;* small, we just clear R4 and RSB to leave. If we have TOO many records then *P;* we go back through and find the last few we need returning the address of *P;* the records to print out in R4. If the number of records is just we return *P;* the address of the records in R4. If the number of records is too little *P;* we save the valid record in R6 and clear R4. *P;* *P;* R1 - Address to start trapesing through memory *P;* R3 - Address of EOF byte in file *P;* R4 - Address of current memory location *P;* R5 - record count *P;* R6 - Address of first valid record found *P;*******************************************************************************; Gtrapse: movl r1, r4 ; Save current location*? clrl r5 ; Clear counter ; !trapse_loop: movzwl (r4)+, r0*E addl2 r0, r4 ; Move to next recordu? blbc r4, 10$ ; Even address?sB incl r4 ; No, make it evenH10$: incl r5 ; We have another record@ cmpl r4, r6 ; Compare to EOFB blss trapse_loop ; Still less than?;eA beql 20$ ; Did we hit EOF?  clrl r5 clrl r4F rsb ; Just leave, not good; P20$: movl r1, r6 ; Save for ending TRAPSE compareL addl2 r5, r7 ; Add to total records foundO cmpl r7, number ; Check against number of linesfM bgtr shorten ; More than we need, cut downEF beql success ; Exactly what we want;0G clrl r5 ; Clear for cleanliness D clrl r4 ; Not enough records: rsb ; Leave...; Fsuccess: movl r1, r4 ; Records are accurateJ rsb ; Leave with print address;*Kshorten: subl2 number, r7 ; Find how many we are overtJ movl r1, r4 ; Start at beginning again!10$: movzwl (r4)+, r0 E addl2 r0, r4 ; Move to next recordt? blbc r4, 15$ ; Even address?rA incl r4 ; No make it evensN15$: sobgtr r7, 10$ ; Until we have number we want;S? rsb ; End of Trapsei;w; ;P;*******************************************************************************P;* Print routines *P;* *P;* For each of the supported formats, these routines print out the *P;* records from the memory location in R4 on down. *P;* *P;* LATER make characters printable. *P;* *P;* R0 - Scratch *P;* R2 - Address of RAB *P;* R3 - Address of EOF byte *P;* R4 - Address of current memory location *P;* R5 - Byte to locate for STREAM files *P;*******************************************************************************; Eprint_records: movab out_rab, r2 ; Save our output RAB M tstb printed ; Did we print a file before?  beql 30$ ;l< clrw RAB$W_RSZ(r2) ; Blank lineB bsbw put_record ; Print blank lineE blbs r0, 10$ ; Did an error occur? B rsb ; Leave if one did; K10$: movzbw sec_ful_desc, RAB$W_RSZ(r2) ; Save file name length L movab sec_ful_name, RAB$L_RBF(r2) ; Save file name addressA bsbw put_record ; Print file namerE blbs r0, 20$ ; Did an error occur?oB rsb ; Leave if one did;o<20$: clrw RAB$W_RSZ(r2) ; Blank lineB bsbw put_record ; Print blank lineE blbs r0, 30$ ; Did an error occur? B rsb ; Leave if one did; G30$: jsb (r8) ; Print out the records L mnegb #1, printed ; We have printed things outF rsb ; End of Print_records;o; ;oDvar_print: movw (r4)+, RAB$W_RSZ(r2) ; Save record lengthE movl r4, RAB$L_RBF(r2) ; Save record address B bsbw put_record ; Print the recordB blbs r0, 10$ ; Test for success brb 30$n; G10$: movzwl RAB$W_RSZ(r2), r0 ; Restore record length G addl2 r0, r4 ; Move past this record*L blbc r0, 20$ ; Is the record length even?G incl r4 ; If not, make it even. 20$: cmpl r4, r3! blss var_print B30$: rsb ; End of Var_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_printsB30$: rsb ; End of Stm_print;A; ;oP;*******************************************************************************P;* Put_record *P;* *P;* This routine simply calls SYS$PUT to send the record to the output *P;*  TAIL.BCK /2%[DCOLIB.TS_SUPPORTED.TAIL]TAIL.MAR;51Q]t5\ file. If an error occurs, it is signaled and R0 is cleared. *P;* *P;* R2 - output RAB *P;*******************************************************************************; Fput_record: $put rab = (r2) ; Write record to file blbc r0, 10$  rsbT;S%10$: pushl RAB$L_STV(r2)n% pushl RAB$L_STS(r2)A$ pushab out_ful_desc pushl #1# pushl #TAIL_WRITEi( calls #5, g^lib$signal clrl r0 rsb ; ; ;  .end tail%*[DCOLIB.TS_SUPPORTED.TAIL]TAIL.OBJ;21+,!X. / 4 ^-20123KPWO 56`@7{@89@7AG HJ3TAILV1-00130-APR-1989 12:22 VAX MACRO V5.0-8 MACRO TAIL  TAIL CLI$GET_VALUE LIB$SIGNALLIB$STOP OTS$CVT_TU_L SYS$CLOSE SYS$CONNECT SYS$CREATE SYS$CRMPSC SYS$DASSGN SYS$DELTVA SYS$EXPREGSYS$OPEN SYS$PARSESYS$PUT SYS$SEARCH TAIL_CLOSETAIL_ILLNUMBER TAIL_NONUMBER TAIL_NONZERO TAIL_NOOUTPUT TAIL_OPEN TAIL_PARSE TAIL_READ TAIL_SEARCH TAIL_UNSUPORG TAIL_UNSUPRFM TAIL_WRITE . ABS .P$ABS$PPPPPRMS_DATAP`Q^QQQQQQLPQNQQ@Q$RMSNAM=P.LISPQQQPP,Q*QQ$`Q^QQQQQ+QLPQNQ@Q$*QP.LISPQQQPDQBQQQ<Q NORMAL_DATAPQQQQ$Q+QQ1Q43INPUTQ&Q A3OUTPUTQ&Q O3LASTQ&Q s3UNDEFINEDQ &Q3FIXEDQ&Q 3VARIABLEQ&Q3VARIABLE with FIXED CONTROLQ&Q!3STREAMQ&Q f3STREAM LFQ &Q3STREAM CRQ &QoCODEP TAIL&klmn`[Z0S0P00Ph031k.<Rb SYS$CLOSEPݢ ݢ# TAIL_CLOSELIB$STOPP= CLI$GET_VALUEPP TAIL_NOOUTPUTLIB$STOP))K CLI$GET_VALUEPP TAIL_NONUMBERLIB$STOPW) OTS$CVT_TU_LPP)TAIL_ILLNUMBERLIB$STOPW TAIL_NONZEROLIB$STOPm/m0 CLI$GET_VALUEPPlEl4k SYS$PARSEP*ݫ ݫ TAIL_PARSE LIB$SIGNALlm1P  퐠 5 Q(Q 0k SYS$SEARCHP6lmPʂ1:ݫ ݫ TAIL_SEARCH LIB$SIGNAL1PkSYS$OPENPݫ ݫ TAIL_OPEN LIB$SIGNAL1_< Y0P'7Oa|@89@7AG HJP!*******************************************************************************P!* Title: TAIL_MSG *P!* *P!* Version: 2-001 *P!* *P!* Facility: TAIL *P!* *P!* Abstract: This module contains the error messages used by TAIL. *P!* *P!* Environment: Message utility, no privilige necessary. *P!* *P!* Author: Peter A. Portante, 1989 *P!*******************************************************************************..title TAIL_MSG - Tail error messages.facility TAIL,1.severity ERROR<PARSE /fao=14SEARCH /fao=1.OPEN /fao=1/CREATE /fao=11WRITE /fao=13READ /fao=1.CLOSE /fao=1  TAIL.BCK1!h2([DCOLIB.TS_SUPPORTED.TAIL]TAIL_MSG.MSG;5P BUNSUPORG /fao=1CUNSUPRFM /fao=1.severity FATAL0NOOUTPUT /fao=05NONUMBER /fao=0>ILLNUMBER /fao=1ENONZERO /fao=0.end(*[DCOLIB.TS_SUPPORTED.TAIL]TAIL_MSG.OBJ;2+,6!./ 4QF-20123KPWO56x>7|@89@7AG HJ2TAIL_MSG027-APR-1989 13:2027-APR-1989 13:20VAX-11 Message V04-00k$ABS$ MSG$SECTIONMSG$AAAAAAAAAAAMSG$AAAAAAAAAABMSG$AAAAAAAAAAC/l TAIL_NONZEROdTAIL_ILLNUMBER\ TAIL_NONUMBERT TAIL_NOOUTPUTJ TAIL_UNSUPRFMB TAIL_UNSUPORG: TAIL_CLOSE2 TAIL_READ* TAIL_WRITE" TAIL_CREATE TAIL_OPEN TAIL_SEARCH  TAIL_PARSETAIL$_FACILITYQPeP*P'P(p{  (H0r8@HP>Xj`h4PARSE$error parsing file specification !AS.SEARCHerror searching for file !AS&OPENerror opening file !AS(CREATEerror creating file !AS*WRITEerror writing to file !AS*READerror reading from file !AS&CLOSEerror closing file !AS>UNSUPORG*file organization of !AS is not sequential>UNSUPRFM+!AS record format not supported in file !AS,NOOUTPUTno output file specified0NONUMBERno number of lines speicified: ILLNUMBER&Illegal decimal number specified "!AS"@NONZERO-number of lines must be greater than zero (0)TAIL