.enable substitution .disable display ; TAIL.BLD - (TAIL.TSK) - View the tail end of a file ; Copyright (C) 1995 John Otis Lene Comeau ; ; This program is free software; you can redistribute it and/or modify ; it under the terms of the GNU General Public License as published by ; the Free Software Foundation; either version 2 of the License, or ; (at your option) any later version. ; ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; GNU General Public License for more details. ; .; You should have received a copy of the GNU General Public License .; along with this program; if not, write to the Free Software .; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. .; .; A copy of the GNU General Public License is appended to the end of .; this command file. .; .; John Lene-Comeau, ICT, POB 100632, Ft. Lauderdale, FL 33310-0632 .; jcomeau@world.std.com or CompuServe: 70641,57 .; .;Build file using IND under RSX11M 4.0 or later .;TAIL.BLD - (TAIL.TSK) - Show a screenful from the end of a file .; To generate TAIL.TSK - @TAIL.BLD .; To use: INS TAIL .; TAIL [OUTDEV:OUTFILE.EXT] [/F] = [INPDEV:INPUT.EXT] [/B] .; /F switch, as in *nix, causes output to continue until terminated by keyhit .; (input file stays open, waiting for updates) .; /B switch causes input file to be treated as binary, forcing output to HEX .;2/10/95: x01.00 - alpha release .;2/23/95 x01.01 - first bugfix .setf debug .if p1 eq "DEBUG" .sett debug .gosub macbld .gosub tkbbld ;Assembling TAIL.MAC... .if ne "DCL" .ifnins mac mac tail .if ne "DCL" .ifins mac mac tail=tail .if eq "DCL" mac tail .if ne &7 .exit ;Linking TAIL.TSK... link @tail.tkb .exit .; .macbld: ;Building MACRO-11 source... .open tail.mac .ift debug .data debug=1 .enable data .title TAIL - Display the tail end of a file (like the *nix utility) ;Author John Comeau, jcomeau@world.std.com ;Environment RSX-11M V4.0 or greater .ident /x01.01/ ;first bugfix - /F now works most of the time .enable mcl ;let MAC do the work of macro lookups ; .title JLCMACS - macros for commonly needed tasks ;author: John Comeau, ET Ft. Lauderdale ;2/17/95 rewrote PRINT macros to use separate PSECT for the code, inserting ; only a CALL statement inline. This makes it easier to pepper the code with ; PRINT statements for debugging purposes; also added DBGMSG for that purpose, ; i.e., if DEBUG is defined, PRINT the message. ; .macro push arg mov arg,-(sp) .endm push ; .macro pop arg mov (sp)+,arg .endm pop ; .macro symbol text,number ;make a unique symbol with letters + number text''number: .endm symbol ; .macro getsym text,number,dest ;get symbol into destination mov #text''number,dest .endm getsym ; .macro callsym text,number ;make call instruction from symbols call text''number .endm callsym ; .macro message string .iif ndf msgnum, msgnum=0 .save ;current program section .pdata data ;switch to data section msgnum=msgnum+1 ;update message number symbol msg,\msgnum .asciz |string| .even ;so any word data later doesn''t get byte-aligned .restore ;back to previous program section .endm message ; .macro print string,p1,p2,p3,p4,p5,p6,p7,p8,p9 message <%2R -- string> c=0 ;arg count callsym prt,\msgnum ;call routine to print specific message .save ;current .PSECT context .pcode print symbol prt,\msgnum ;create symbol for calling here ror -(sp) ;save carry flag on stack push r2 ;save working registers push r1 push r0 push $dsw ;save status word .irp arg, .if nb arg push arg ;push any parameters c=c+1 .endc .endr push tskbuf+2 ;taskname in radix-50 push tskbuf ;two words mov sp,r2 ;save parameter list location getsym msg,\msgnum,r1 ;point to the string add #-132.,sp ;output buffer mov sp,r0 ;place address in r0 call $edmsg ;call system formatting routine mov sp,r0 ;get address of formatted text again .iif ndf qioefn, qioefn=1 .iif ndf tiodev, tiodev=5 ;use TI: if not otherwise specified .mcall qiow$s qiow$s #io.wvb,#tiodev,#qioefn,,,, add #132.,sp ;adjust SP for output buffer add #+4,sp ;and for parameter list + rad50 taskname pop $dsw ;restore status word pop r0 ;restore registers pop r1 pop r2 rol (sp)+ ;restore carry flag return .restore ;previous .PSECT .endm print ; .macro dbgmsg string,p1,p2,p3,p4,p5,p6,p7,p8,p9 .iif df debug, print ,p1,p2,p3,p4,p5,p6,p7,p8,p9 .endm dbgmsg ;print message only if DEBUG is defined ; .macro stopfor p1,p2,p3,p4,p5,p6,p7,p8,p9 ;to allow use of flag names rather than having to calculate bitmask separately .mcall stlo$s,stse$s .narg argc ;which macro to use depends on arg count .if eq argc-1 ;if only one arg... stse$s #p1 ;...easy as pie .iff mask=0 ;start off with blank bitmask .irp flag, .if nb flag bit=1 .rept flag - 1 ;for example, for flag 1, don''t repeat at all bit=bit*2 .endr mask=mask!bit .endc .endr stlo$s 0,#mask ;assume group 0 flags .endc .endm stopfor ;exits here if 9 args ; .macro waitfor p1,p2,p3,p4,p5,p6,p7,p8,p9 ;to allow use of flag names rather than having to calculate bitmask separately ; copied from my STOPFOR macro .mcall wtlo$s,wtse$s .narg argc ;which macro to use depends on arg count .if eq argc-1 ;if only one arg... wtse$s #p1 ;...easy as pie .iff mask=0 ;start off with blank bitmask .irp flag, .if nb flag bit=1 .rept flag - 1 ;for example, for flag 1, don''t repeat at all bit=bit*2 .endr mask=mask!bit .endc .endr wtlo$s 0,#mask ;assume group 0 flags .endc .endm waitfor ;exits here if 9 args ; .iif ndf id, id=1 ;program number using this error number convention ; ;error macros require subroutines similar to the following: ;accerr: ; bcc 99$ ;return if no error ; print ; sec ;refresh error flag ;99$: return ;back to caller ; .macro error label .iif ndf errnum, errnum=0 ;inc error number after each ERROR macro sec ;set error flag call label ;not really a call but a jump, used in conj w/ABORT errnum=errnum+1 .endm error ; ;alternate form ERRCHK - if C flag not set, return, otherwise same as ERROR ; Why would anyone use such a thing? Why not just BCS to the error routine? ; In my experience, as programs are changed the error routines go out of the ; 128-word branch limit, and then schlock rewrites have to be done. Using a ; CALL to an error checking routine means no rewrites, since you have the full ; virtual address space. .macro errchk label .iif ndf errnum, errnum=0 ;inc error number after each ERROR macro call label errnum=errnum+1 .endm errchk ; .macro abort severity pop r4 ;get PC from CALL used to get here print ,r4,$dsw exst$s #!!severity .endm abort ; .macro begin label ;use before every routine or subroutine .if nb label ;if label not already tagged label: .endc .enable lsb ;local symbol block, allows you to .PSECT all over the ;place without losing sight of local labels .endm begin ; .macro end label ;end every routine or subroutine with this .dsable lsb ;end the local symbol block .endm end ; .macro prginit label ;initialization common to JLC type programs .enable mcl ;enable automatic lookup of macros .pcode code .if nb label label:: .iff main:: .endc .idata impure tskbuf: .blkw 16. ;buffer for GTSK$ data .pcode code .mcall gtsk$s gtsk$s #tskbuf ;get our task''s data .enable lsb ;don''t need to use BEGIN macro for routine MAIN .endm prginit ; .macro jret label ;to pacify the idiots who insist on a common ;return point from each subroutine return ;just return from here, don''t tell anybody .endm jret ; ; .list me ;show full expansion of macro .macro incl arg,?skip ;Two methods are used: ADD #1,DEST followed by ADC DEST+2, and ; INC DEST ;BNE .+2; INC DEST+2 ;The second is faster (using LSI-11 timing, 4.90+4.90+3.50=13.30 usec ; with no rollover, vs. 4.20+1.40+4.90+4.90+4.90=20.3 usec for the ADD, ADC.) ;When it rolls over, the first method is slower by 2.8 usec due to the extra ; INC (all figures assuming mode 6 for DEST), but since this happens only ; 1/65535 of the time, this is negligible. So, defining JLC makes the INCL ; macro about 35% faster. .if df jlc inc arg ;inc the low word bne skip ;if it rolled over, it set the Z flag .iff add #1,arg ;increment by one, set carry on rollover .endc ;If it is a mode 1 argument, for example (R2), the following word ; must be expressed as 2(R2) and not (R2)+2. .ntype argtyp,arg .if ne 7- ;if NOT one of the PC modes .if eq 10- ;mode 1, (ER) .if df jlc inc 2''arg skip: .iff adc 2''arg .endc .mexit .endc .endc .if ne 60- ;anything but mode 6, E(ER), or E if PC mode .error ;arg cannot be used with INCL macro .iff .if df jlc inc 2+arg skip: .iff adc 2+arg .endc .endc .endm incl ; .macro swstk$ label ;in case macro not in system MLB''s emt 376 ;trap to $EMSST in DRDSP .word label .endm swstk$ ; .macro even direction,word .if idn ror word ;shift low bit into carry adc word ;add that bit to shifted word rol word ;now roll it back to where it was, rounded up .mexit .endc ;up .if idn bic #1,word ;somewhat easier .endc ;down .endm even ; cr=13. ;carriage return lf=10. ;line feed space=32. ;" " tab=9. ;^I esc=27. ; character dcs=144. ;P csi=155. ;[ st=156 ;\ invert=2 ;inverse video switch ticks=1 ;mrkt$ constants seconds=2 minutes=3 hours=4 msgnum=0 ;reinitialize these, because MAC inc''ed them as it read in this file errnum=0 ; .idata impure exstat: .word ex$err ;exit status gcml=1 ;using the library routines provided cmdblk: gcmlb$ 1,,,5 prompt: .ascii "TAIL>" prlen=.-prompt .even csi$ ;define control block offset .even ;docs show this necessary csiblk:: .blkb c.size ;set up control block .even ;just in case fsrsz$ 4 ;buffer for GCML$ file, input, and output inpfdb:: fdbdf$ ;define input FDB fdop$a 1,,definp,fo.rd!fa.shr ;LUN 1, default input device SY: fdrc$a fd.rwm ;allow block I/O fdbk$a readbf,<512.*3> ;3-block read buffer outfdb:: fdbdf$ ;define output FDB fdop$a 2,,defout ;LUN 2, default device TI0: fdat$a r.var,fd.cr defout:: nmblk$ TAIL,DAT,,TI,0 ;specify TI0: as default output device definp:: nmblk$ ,LOG,,SY,0 ;specify SY0: as default input device, type LOG bmode=1 ;define binary mode fmode=2 ;and file-remains-open mode help=4 swtabl:: csi$sw b,bmode,swmask ;specify binary input file (hex ASCII output) csi$sw f,fmode,swmask ;leave file open and continue showing updates csi$sw h,help,swmask ;show usage hints csi$nd ;end of switch table swmask:: .blkw 1 ;word to store switch bitmask curblk:: .blkw 2 ;current block to be read endblk:: .blkw 2 ;end of file block endbyte:: .blkw 1 ;first free byte in EOF block bufend:: .blkw 1 ;pointer to end of buffer recsiz:: .blkw 1 ;size of fixed-length records newrecs:: .blkw 1 ;in /F mode, new records since last .POINT eoferrs:: .blkw 1 ;false EOF errors received in /F mode .idata $$zbuf ;final psect, for EXTK$ buffer space readbf:: .psect ;to previous program section ; .pcode code prginit ;use JLC program structure (such as it is) finit$ ;initialize FCS structures extk$s #<<5*256.>/32.> ;request 5 blocks from the operating system errchk 100$ 10$: gcml$ #cmdblk,#prompt,#prlen ;get the command line errchk 40$ ;quit on error bcs 10$ ;if we returned, try again tst cmdblk+g.cmld ;anything there? beq 10$ ;try again if not clr swmask ;clear switch bitmask csi$1 #csiblk,cmdblk+g.cmld+2,cmdblk+g.cmld ;initial parse errchk 50$ ;if error, show user and exit csi$2 r0,output,#swtabl ;parse output filespec errchk 60$ ;trap any error open$w #outfdb,,#csiblk+c.dsds ;open the output file errchk 70$ ;trap any error bitb #cs.equ,csiblk+c.stat ;was there an equals sign? beq 16$ ;if not, bypass next parse csi$2 #csiblk,input,#swtabl ;parse input filespec errchk 80$ ;trap any error 16$: opns$r #inpfdb,,#csiblk+c.dsds ;open the input file for shared access errchk 90$ ;trap any error call tail ;perform the I/O dbgmsg close$ #inpfdb ;close input and output files dbgmsg close$ #outfdb ;... dbgmsg bit #fmode,swmask ;was /F switch used? beq 18$ ;skip if so, access mode wasn''t changed... fdrc$r #inpfdb,#fd.rwm ;allow block I/O fdbk$r #inpfdb,#readbf,#<512.*3> ;3-block read buffer dbgmsg 18$: jmp 10$ ;loop back around for another command 20$: exst$s exstat ;return with status 40$: bcc 49$ ;return if no error movb g.err(r0),exstat ;copy error byte into exit status cmpb #ge.eof,exstat ;was it EOF? beq 46$ ;skip if so, exit with no error indication print <*FATAL* - Error retreiving command line = %B>,#exstat jmp 20$ ;exit 46$: movb #ex$suc,exstat ;if EOF, exit with success indication jmp 20$ ;... 49$: return 50$: bcc 59$ ;return if no error print tst (sp)+ ;toss the return address jmp 10$ ;and loop around back to top 59$: return 60$: bcc 66$ ;return if no error print 64$: tst (sp)+ ;toss the return address jmp 10$ ;and loop around back to top 66$: dbgmsg ,swmask bit #help,swmask ;was help requested? beq 69$ ;return if not call helpem ;otherwise oblige the user br 64$ ;don''t process any further 69$: return 70$: bcc 79$ ;return if no carry movb f.err(r0),exstat ;get FCS error number print ,#exstat call showfn ;show the filename tst (sp)+ ;toss the return address jmp 10$ ;and loop around back to top 79$: return 80$: bcc 89$ ;return if no error print close$ #outfdb ;make sure to close output file tst (sp)+ ;toss the return address jmp 10$ ;and loop around back to top 89$: return 90$: bcc 99$ ;return if no carry movb f.err(r0),exstat ;get FCS error number print ,#exstat call showfn ;show the filename close$ #outfdb ;make sure to close output file tst (sp)+ ;toss the return address jmp 10$ ;and loop around back to top 99$: return 100$: bcc 109$ ;return if no error print <*FATAL* - System would not grant request for buffer space> jmp 20$ ;exit 109$: return end main ; helpem:: begin dbgmsg dir$ #10$ ;Using $C form here had TKB so screwed up it lost all track of the code seg return 10$: qiow$ io.wvb,6,1,,,, ;show help message end helpem ; .save ;psect context .pdata text helptxt: .ascii ; 1 2 3 4 5 6 ; 0123456789012345678901234567890123456789012345678901234567890 .ascii "Usage: TAIL [OUTDEV:OUTFILE.EXT=] INFILE.EXT [/B] [/F]" .ascii "TAIL is similar to the Unix utility of the same name;" .ascii " It shows about a screenful" .ascii "of data from the end of a file. The default output is to your" .ascii " terminal screen:" .ascii "TAIL [1,4]CONSOLE/F""will show the system logfile on " .ascii "your screen, and update it dynamically. You can" .ascii "override this default by using the format" .ascii "TAIL LP0:=[1,4]CONSOLE (for example)" .ascii "which will send the last few lines of SY0:[1,4]CONSOLE.LOG to" .ascii " the line printer." .ascii "To send it to a file, you must specify a device name to " .ascii "override the default:" .ascii "TAIL SY0:TEMP.DAT=TAIL.TSK/B" .ascii "will send a HEX dump of the last 2 blocks of TAIL.TSK to a " .ascii "file called TEMP.DAT." .ascii "Defaults: TI0:TAIL.DAT=SY0:.LOG, meaning that a logfile is " .ascii "the default input." .ascii "Control-Z to exit - meaning, hold the key " .ascii "down and hit Z." helplen=.-helptxt .even .restore ; tail:: begin call getend ;get the end-of-file markers stored in globals errchk 100$ ;make sure it went OK mov #3,r1 ;assume at least 3 blocks in the file mov endblk,curblk ;store end-of-file block number... mov endblk+2,curblk+2 ;in our current block number register tst curblk ;anything in high word? bne 10$ ;continue if so cmp endblk+2,r1 ;is end-of-file block at least 3? bhis 10$ ;continue if so mov endblk+2,r1 ;otherwise use its number ;The block number is 1-based. So if there''s only 1 block in the file, ; the end-of-file block will be one. Let''s say the end-of-file block is 5. ; We want to read blocks 3, 4, and 5. Therefore we subtract 2, which is the ; number of blocks desired less 1. 10$: dec r1 ;final block may not be complete, don''t count it sub r1,curblk+2 ;subtract number-of-blocks from current block sbc curblk ;... ash #9.,r1 ;multiply by 512. add endbyte,r1 ;add offset of first free byte to number in R1 read$ r0,#readbf,r1,#curblk ;grab the end of the file errchk 80$ ;quit on error wait$ r0 ;wait for I/O to complete ;leave out the above statement for interesting intermittent screen vomits movb f.rtyp(r0),r2 ;get record type so we know how to process it errchk 60$ ;error if empty bitb #fd.ftn!fd.blk,f.ratt(r0) ;FORTRAN carriage control or ;cannot span block boundaries? errchk 50$ ;unsupported if so, crap out cmp r2,#r.var ;above R.VAR? errchk 70$ ;error if so, only R.FIX and R.VAR accepted beq 40$ ;continue if variable records callr fixtail ;use other routine for fixed-length records 40$: tstb f.rsiz+1(r0) ;record size more than 255 chars? errchk 90$ ;error if so, need zero in length word for this to work callr vartail ;use routine for variable-length records ;error check routines 50$: beq 79$ ;return if neither FD.FTN nor FD.BLK is set br 72$ ;otherwise show unsupported 60$: bne 79$ ;return if anything there br 72$ ;else show message 70$: blos 79$ ;return if R.FIX or R.VAR 72$: print tst (sp)+ ;toss the return address 79$: return ;return to main routine 80$: bcc 89$ ;return if carry clear mov #inpfdb+f.err,r1 print ,r1 tst (sp)+ ;toss return address 89$: return ;and return to main routine 90$: beq 99$ ;return if zero print tst (sp)+ ;toss return address 99$: return ;and return to main routine end tail 100$: bcc 109$ ;return if OK tst (sp)+ ;error message already printed by GETEND routine 109$: return ;to main routine ; showfn:: begin ;Issue an informational message showing the filename string from the FNB ; pointed to by the FDB in R0 call $saval ;save registers mov sp,r5 ;save SP here push f.fnb+n.fver(r0) ;start creating arblock with version number push f.fnb+n.ftyp(r0) ;extension push f.fnb+n.fnam+4(r0) ;filename push f.fnb+n.fnam+2(r0) ;... push f.fnb+n.fnam(r0) ;... push f.fnb+n.unit(r0) ;device unit number push #f.fnb+n.dvnm ;ASCII device name add r0,(sp) ;make it a valid address mov sp,r2 ;pointer to argblock sub #40.,sp ;make space for output string mov sp,r0 ;point R0 to it push r0 ;save pointer mov #fnmfmt,r1 ;format string address call $edmsg pop r0 ;restore pointer print ,r1,r0 mov r5,sp ;clean off stack the easy way return ;back to caller .save .idata impure fnmfmt:: .asciz "%2A%Q:%X" ;specify format string for filename .even .restore end showfn ; fixtail:: begin ;Called with R1 offset to first free byte in buffer ;first order of business is to back up just enough records so as to fill a ;screen... if record size is such that we will either have too much or too ;little, err on the high side... mov f.rsiz(r0),r2 ;record size into R2 even up r2 ;round up if odd mov r2,recsiz ;store rounded-up record size mov #80.,r3 ;assume ASCII mode bit #bmode,swmask ;then check if not beq 10$ ;continue if ASCII mov #38.,r3 ;and set 38 chars-per-line otherwise 10$: mov r2,r5 ;record size into LSBs clr r4 ;clear MSBs div r4,r3 ;divide by bytes-per-line to get lines-per-record tst r5 ;any remainder? beq 20$ ;skip if not inc r4 ;else allow one more line for that ;In the future, I can check if output device is a terminal ; [bitb #fd.tty,f.rctl(r0)] then, if so, do a QIO$C SF.GMC and get TC.LPP ; (lines-per-page) for a device-specific output. For now, I'll just assume 24. ; Should also get TC.WID (terminal width) for above computation. 20$: mov r4,r3 ;lines-per-record into R3 clr r4 ;now clear MSBs mov #24.,r5 ;lines-per-screen... div r3,r4 ;divided by lines-per-record = records/screen tst r5 ;any remainder? beq 30$ ;skip if not inc r4 ;else, as we said above, err on the high side 30$: mul r2,r4 ;bytes/record * records/screen = bytes/screen mov r1,r3 ;copy bytes available into R3 sub r5,r1 ;subtract bytes/screen from bytes available bcc 40$ ;continue if no borrow 34$: add r2,r1 ;otherwise add records till we go to or above zero bmi 34$ ;loop till non-negative 40$: ;now we''re ready to start creating the output... ;Let the output device handle the breaking up of long records into separate ; lines, which most terminals do by default. mov f.rsiz(r0),r4 ;get actual record size, in case it''s odd mov #outfdb,r0 ;done with input, point R0 to output FDB add #readbf,r1 ;change R1 and R3 from offsets into actual addresses add #readbf,r3 ;... 50$: bit #bmode,swmask ;binary mode set? call 80$ ;convert to Hex ASCII first, if so put$ r0,r1,r4 errchk 100$ ;trap any error and restore add recsiz,r1 ;forward to start of next record cmp r1,r3 ;at end yet? blo 50$ ;loop if not bit #fmode,swmask ;leave file open awaiting more records? bne 60$ ;continue if so return ;else back to caller (carry must be clear from CMP R1,R3) 60$: qiow$s #io.ata,#6,#5,,,,<#chrast> ;attach terminal with unsolicited ;character AST errchk 130$ ;check for any error ;let the AST just set a flag, we'll wait for it later mov #inpfdb,r0 ;point to input file FDB call close ;close, saving FDB for later reopen via FID errchk 220$ ;make sure it closed OK fdrc$r r0,#fd.plc,#readbf,recsiz ;attempt switch to rec mode opns$r r0 ;now reopen the file errchk 90$ ;check how it went, abort on failure dbgmsg clr eoferrs ;clear count of false end-of-file errors br 78$ ;skip to end of loop 70$: mov #inpfdb,r0 ;point to input FDB clr newrecs ;clear count of new records call getattr ;get updated attributes call rewind ;attempt to solve problem detailed below ;With fixed-length records, this always seems to return what''s exactly one ; block (512. bytes) BEFORE the desired record. With variable-length records ; it works fine. 2/24/95 NOPE, had to insert a REWIND for that, too. ;Inserting a REWIND operation seems to have solved the problem. call fastfwd ;position to last known EOF 72$: mov #inpfdb,r0 ;point to input FDB get$ r0 ;grab the next record errchk 180$ ;see if we could clef$s #3 ;was a valid record received? cmp $dsw,#is.set ;testing... bne 78$ ;loop if not inc newrecs ;up the count of new records processed this cycle mov inpfdb+f.nrbd+2,r1 ;buffer pointer mov inpfdb+f.nrbd,r4 ;buffer size bit #bmode,swmask ;binary mode requested? call 80$ ;convert to HEX if so put$ #outfdb,r1,r4 ;send to output errchk 200$ ;test for errors, quit if found br 72$ ;keep trying till we reach EOF 78$: mrkt$s #4,#1,#seconds ;wait 1 second before attempting another GET$ errchk 230$ ;make sure it executed OK dbgmsg waitfor 2,4 ;wait for timeout or keyhit cmkt$s ;cancel timer if it didn''t time out clef$s #2 ;was a key hit? cmp $dsw,#is.set ;testing... errchk 190$ ;back to main loop if so jmp 70$ ;loop back around for more ;convert binary output to HEX ASCII, using buffer address in R1 and length in R4 80$: beq 89$ ;return if binary mode not requested mov #readbf+<3*512.>,r5 ;point to buffer space at end of task push r5 ;save that address for later push r4 ;save current length value on stack... push r1 ;string pointer call hexify ;use common routine pop r5 ;swap original R5 and R1, string pointer goes here pop r4 ;length of string pop r1 ;pointer to hex string asl r4 ;multiply by two for length of hex string 89$: return ; 90$: ;check for errors after reopening bcc 99$ ;skip if no error mov #inpfdb+f.err,r1 print ,r1 tst (sp)+ ;discard the return address 99$: return ; 100$: bcs 120$ ;skip ahead if I/O error bit #bmode,swmask ;binary mode set? bne 110$ ;skip if so return 110$: mov r5,r1 ;restore value of R1 and R4 before continuing... asr r4 ;back to normal after BMODE mucking return ; 120$: ;test for failure in PUT$ to output device mov #outfdb+f.err,r1 print ,r1 sec ;indicate error to caller tst (sp)+ ;toss the return address return ;and return to main routine ; 130$: ;test for failure in ATTach request dbgmsg bcc 139$ ;return if OK print ,$dsw tst (sp)+ ;toss the return address 139$: return ;to main routine ; 140$: qiow$s #io.det,#6,#1 ;detach the terminal first bcc 144$ ;skip if executed OK print <*FATAL* - Could not detach terminal, $DSW=%P>,$dsw exst$s #ex$sev 144$: dbgmsg 149$: return ;to main routine ; 150$: ;check for valid record obtained from GET$ call dbgmsg bcs 149$ ;error, just return setf$s #3 ;set event flag to show valid record call $saval ;save registers call .mark ;then get pointer to next record mov r1,endblk ;update global pointers mov r2,endblk+2 ;... mov r3,endbyte ;... 159$: return ; 160$: ;test for set flag indicating keyhit bne 159$ ;return if not tst (sp)+ ;toss the return address dbgmsg jmp 140$ ;detach terminal before returning 169$: return ;return to main routine ; 170$: ;test for errors after PUT$ dbgmsg bcc 179$ ;return if OK movb f.err(r0),exstat ;get error byte print ,#exstat tst (sp)+ ;remove return address jmp 140$ ;detach terminal before returning 179$: mov r5,r1 ;restore value of R1 and R4 before continuing... asr r4 ;back to normal after BMODE return ; 180$: call chkget ;check validity of last GET$ and report discrepancies bcc 189$ ;return if no major problem tst (sp)+ ;otherwise return to main loop jmp 140$ ;after detaching terminal 189$: return ; 190$: ;test for set flag indicating keyhit bne 189$ ;return if not tst (sp)+ ;toss the return address jmp 140$ ;detach terminal before returning 199$: return ;return to main routine ; 200$: ;test for errors after PUT$ bcc 209$ ;return if OK movb f.err(r0),exstat ;get error byte print ,#exstat tst (sp)+ ;remove return address jmp 140$ ;detach terminal before returning 209$: mov r5,r1 ;restore value of R1 and R4 before continuing... asr r4 ;back to normal after BMODE return ;to main routine 220$: ;check if CLOSE$ executed properly bcc 229$ ;return if so movb f.err(r0),exstat print ,#exstat tst (sp)+ ;discard return address so it returns to main loop 229$: return 230$: ;see if MARK TIME was initiated bcc 239$ ;OK if carry clear print ,$dsw tst (sp)+ ;don''t continue, return to main loop 239$: return end fixtail ; chkget:: begin ;check for valid record obtained from GET$ call maxeof=20. ;define maximum false EOF errors before quitting bcc 10$ ;if no error, just return after setting success flag dbgmsg ,#inpfdb+f.err cmpb f.err(r0),#ie.eof ;end-of-file? beq 20$ ;return if so, after saving EOF marker mov #inpfdb+f.err,r1 print ,r1 br 90$ ;set error flag and return 10$: setf$s #3 ;set event flag to show valid record return ;no need to update EOF marker until we fail on GET$ 20$: call chkeof ;see if EOF matches what''s saved bhi 50$ ;skip if more beq 40$ ;continue if same dbgmsg 30$: inc eoferrs ;else inc count of false EOF errors cmp eoferrs,#maxeof ;test if reached maximum blo 70$ ;return if not print br 90$ 40$: ;here on EOF error, and EOF matches what it was last time tst newrecs ;any new records received? beq 70$ ;if not, it matches our info, just return dbgmsg br 30$ ;quit, we lost synchronization 50$: tst newrecs ;we should have gotten some records, since EOF moved bne 60$ ;if so, it matches our data, so update EOF and return dbgmsg ,eoferrs inc eoferrs ;else inc count of false EOF errors cmp eoferrs,#maxeof ;test if reached maximum blo 70$ ;return if not print br 90$ ;return indicating severe error 60$: call getend ;save new EOF markers clr eoferrs ;clear EOF error count 70$: clc ;clear error flag return 90$: sec ;indicate severe error and return return end chkget ; chrast:: begin setf$s #2 ;indicate keyhit tst (sp)+ ;remove top of stack astx$s ;return from AST end chrast ; chkeof:: begin ;see how saved EOF compares with what''s in FDB, return with flags from CMP .if df debug mov r0,100$ ;then make word pointer add #f.efbk,100$ ;...to end-of-file block longword .endc ;debug dbgmsg ,100$,#endblk cmp f.efbk(r0),endblk ;check high word first bne 99$ ;quit on first difference... cmp f.efbk+2(r0),endblk+2 ;lsb''s... bne 99$ ;... dbgmsg ,f.ffby(r0),endbyte cmp f.ffby(r0),endbyte ;first free byte 99$: return 100$: .iif df debug, .blkw 1 end chkeof ; getattr:: begin ;Refresh file attributes in FDB <- R0 using QIO ;Procedure used is from HELP FCS FLUSH, a nice hackerly dissertation jsr r5,.savr1 ;really only need to save regs 1-3 mov #io.rat,r1 ;read attributes mov #2,r2 ;number of DPB parameters mov #50$,r3 ;address of those parameters mov r0,(r3) ;store FDB address add #f.fnb+n.fid,(r3) ;then add offset to FID mov r0,70$ ;FDB address is where attributes are to be stored callr .xqio ;perform the QIO (and wait) 50$: .blkw 1 ;FID address goes here .word 60$ ;parameter list address 60$: .byte -4,s.fatt ;read user file attributes 70$: .blkw 1 ;FDB address goes here .word 0 ;end of list end getattr ; vartail:: begin ;start at beginning of raw data and look for something that resembles a count ; word, then follow that trail and see if it leads us to the end of the buffer. ;If not, start where we left off and keep trying. mov r1,bufend ;save end of buffer add #readbf,bufend ;make it absolute mov #readbf,r1 ;now point to top of buffer clr r5 ;clear count of records 10$: tstb 1(r1) ;is following byte null? beq 20$ ;skip ahead if so 12$: tst (r1)+ ;else skip to next word cmp r1,bufend ;at the end? bne 10$ ;loop if not print return 20$: tst (r1) ;is this whole word null? beq 12$ ;if so, skip it mov r1,r2 ;save our place while we test this path... 22$: mov (r1)+,r3 ;get supposed count word and inc pointer cmp #400,r3 ;must fit in one byte, or it''s no good as count blos 50$ ;skip if that''s the case inc r5 ;count of records found even up r3 ;in case of odd-sized record add r3,r1 ;add size of record to pointer, point to next record cmp r1,bufend ;are we at the end of the buffer? bhi 50$ ;if over, we went down the wrong trail for sure blo 22$ ;if under, keep going ;if we reached here, we found the correct path... now skip records as necessary 40$: cmp r5,#24. ;too many records? blos 54$ ;skip if not, start showing them sub #24.,r5 ;get number of records to skip 44$: mov (r2)+,r3 ;get count word and advance pointer even up r3 ;in case of odd-size record add r3,r2 ;point to next record sob r5,44$ ;loop till done br 54$ ;go show what we have 50$: mov r2,r1 ;restore pointer to where it was before we went wrong clr r5 ;reinitialize the record count br 12$ ;loop till good trail found or we reach the end 54$: mov r2,r1 ;copy pointer to R1 mov #outfdb,r0 ;done with input device for now 56$: mov (r1)+,r4 ;damn well better be word aligned here... bit #bmode,swmask ;binary mode set? call 80$ ;if so, use other routine put$ r0,r1,r4 ;send the record to output device errchk 100$ ;check for errors, and if binary mode restore registers even up r4 ;round up if odd add r4,r1 ;modify pointer... cmp r1,bufend ;at end of data? blo 56$ ;loop if not bit #fmode,swmask ;leave file open awaiting more records? bne 60$ ;continue if so return ;else back to caller (carry must be clear from CMP R1,R3) 60$: qiow$s #io.ata,#6,#1,,,,<#chrast> ;attach terminal with unsolicited ;character AST errchk 160$ ;check for any error ;let the AST just set a flag, we'll wait for it later mov #inpfdb,r0 ;point to input FDB call close ;close input, save FNB errchk 220$ ;check for errors fdrc$r #inpfdb,#fd.plc,#readbf,#512. ;attempt switch of access mode ;can''t check for error, all it does is move stuff into the FDB opns$r r0 ;reopen input file for record access this time errchk 130$ ;abort if error dbgmsg clr eoferrs ;clear count of false end-of-file errors br 78$ ;skip to end of loop 70$: mov #inpfdb,r0 ;point to input FDB clr newrecs ;clear count of new records call getattr ;refresh user attributes call rewind ;shouldn''t be necessary but is... call fastfwd ;fast forward to last-known EOF 72$: mov #inpfdb,r0 ;point to input FDB again get$ r0 ;grab the next record errchk 180$ ;see if we could clef$s #3 ;was a valid record received? cmp $dsw,#is.set ;testing... bne 78$ ;loop if not inc newrecs ;up the count of new records processed this cycle mov inpfdb+f.nrbd+2,r1 ;buffer pointer mov inpfdb+f.nrbd,r4 ;buffer size bit #bmode,swmask ;binary mode requested? call 80$ ;convert to HEX if so put$ #outfdb,r1,r4 ;send to output errchk 200$ ;test for errors, quit if found br 72$ ;keep trying till we reach EOF 78$: mrkt$s #4,#1,#seconds ;wait 1 second before attempting another GET$ errchk 230$ ;make sure it executed OK dbgmsg waitfor 2,4 ;wait for timeout or keyhit cmkt$s ;cancel timer if it didn''t time out clef$s #2 ;was a key hit? cmp $dsw,#is.set ;testing... errchk 190$ ;back to main loop if so jmp 70$ ;loop back around for more ;convert binary output to HEX ASCII, using buffer address in R1 and length in R4 80$: beq 89$ ;return if not BMODE tst r4 ;null record? beq 89$ ;return if so mov #readbf+<3*512.>,r5 ;point to buffer space at end of task push r5 ;save that address for later push r4 ;save current length value on stack... push r1 ;also string pointer push r2 ;line count call hexify ;translate pop r2 ;line count pop r5 ;swap original R5 and R1, string pointer goes here pop r4 ;length of string pop r1 ;pointer to hex string asl r4 ;multiply by two for length of hex string 89$: return ; 90$: ;check if zero on even boundary is really a null count ;note - this routine assumes entry with C=0 after TSTB bne 99$ ;if odd, no checking necessary tstb 1(r1) ;otherwise make sure following byte is also null beq 99$ ;return if so, it''s a valid (null) record count 92$: print sec ;indicate this unique status 99$: return ; 100$: ;check feedback from PUT$ bcs 120$ ;skip ahead if I/O error bit #bmode,swmask ;binary mode set? bne 110$ ;skip if so return 110$: tst r4 ;null record? beq 119$ ;return if so, nothing was sent mov r5,r1 ;restore value of R1 and R4 before continuing... asr r4 ;back to normal after BMODE mucking 119$: return 120$: mov #outfdb+f.err,r1 print ,r1 sec ;indicate error to caller tst (sp)+ ;toss the return address 129$: return ;and return to main routine ; 130$: ;check for errors after reopening bcc 139$ ;skip if no error mov #inpfdb+f.err,r1 print ,r1 tst (sp)+ ;discard the return address 139$: return ; 160$: ;test for failure in ATTach request bcc 169$ ;return if OK print ,$dsw tst (sp)+ ;toss the return address 169$: return ;to main routine ; 170$: qiow$s #io.det,#6,#1 ;detach the terminal first bcc 174$ ;continue if detached OK print <*FATAL* - Could not detach terminal, $DSW=%P>,$dsw exst$s #ex$sev 174$: dbgmsg 179$: return ;to main routine ; 180$: call chkget ;check validity of last GET$ and report discrepancies bcc 189$ ;return if no major problem tst (sp)+ ;otherwise return to main loop jmp 170$ ;after detaching terminal 189$: return ; 190$: ;test for set flag indicating keyhit bne 189$ ;return if not tst (sp)+ ;toss the return address jmp 170$ ;detach terminal before returning 199$: return ;return to main routine ; 200$: ;test for errors after PUT$ bcc 209$ ;return if OK movb f.err(r0),exstat ;get error byte print ,#exstat tst (sp)+ ;remove return address jmp 170$ ;detach terminal before returning 209$: mov r5,r1 ;restore value of R1 and R4 before continuing... asr r4 ;back to normal after BMODE return ;to main routine ; 220$: ;check if CLOSE$ executed properly bcc 229$ ;return if so movb f.err(r0),exstat print ,#exstat tst (sp)+ ;discard return address so it returns to main loop 229$: return ; 230$: ;see if MARK TIME was initiated bcc 239$ ;OK if carry clear print ,$dsw tst (sp)+ ;don''t continue, return to main loop 239$: return end vartail ; close:: begin ;Close the file whose FDB is in R0 dbgmsg jsr r2,$savvr ;save registers we need mov #f.fnb,r1 ;point to filename block add r0,r1 ;of FDB pointed to by R0 mov #s.fnbw,r2 ;size of filename block 10$: push (r1)+ ;save the filename block on the stack sob r2,10$ ;loop till done close$ r0 ;now close the input file mov #s.fnbw,r2 ;size back into R2 20$: pop -(r1) ;restore the filename block sob r2,20$ ;loop till done return end close ; .if df oldver ;comment this part out for new version reopen:: begin dbgmsg ofid$r #inpfdb ;open it by the file I.D., which was preserved rol -(sp) ;save carry flag mov endblk,r1 ;set up registers for .POINT call mov endblk+2,r2 ;... mov endbyte,r3 ;... dbgmsg ,#endblk dbgmsg ,endbyte call .point ;ignore any errors returned ror (sp)+ ;restore carry flag and return return end reopen .endc ;oldver ; fastfwd:: ;skip to last known end of file mov endblk,r1 ;set up registers for .POINT call mov endblk+2,r2 ;... mov endbyte,r3 ;... dbgmsg ,#endblk dbgmsg ,endbyte callr .point ;ignore any errors returned ; rewind: ;back to beginning of file clr r1 ;clear high word of block number mov #1,r2 ;position to first block clr r3 ;clear byte number callr .point ; getend:: begin ;Attempt to determine end-of-file block and byte. If another program is ; writing into the file faster than we can read it, abort after 10 attempts. ;2/18/95 found out it doesn''t work like that - the attributes are NOT updated ; automatically. So rewrote this without all the checks and balances. mov f.efbk(r0),endblk ;get EOF block from input FDB mov f.efbk+2(r0),endblk+2 ;... mov f.ffby(r0),endbyte ;EOF byte (First Free BYte) return end getend ; hexify:: begin ;HEX ASCII conversion routine. Call with: ; R1 -> Pointer to source string ; R5 -> Pointer to destination string buffer ; R4 -> Length of source string ; R2 - Destroyed, must be saved before calling (as well as R1, R4, and R5) movb (r1),(r5) ;next byte into translation buffer asrb (r5) ;move high nybble into low asrb (r5) ;... asrb (r5) ;... asrb (r5) ;... bicb #360,(r5) ;clear sign-extended bits movb (r5),r2 ;get the number movb 90$(r2),(r5)+ ;and store its Hexadecimal symbol in place movb (r1)+,(r5) ;get the same byte again bicb #360,(r5) ;clear high nybble movb (r5),r2 ;get the number movb 90$(r2),(r5)+ ;store hex symbol sob r4,hexify ;loop till done return 90$: .ascii "0123456789ABCDEF" end hexify ; .end main .disable data .close .return .; .tkbbld: ;Building Link file... .open tail.tkb .ift debug .goto tkbdbg .enable data tail/cp,,tail=tail / task=...tai asg=ti:6 libr=fcsres:ro // .disable data .close .return .tkbdbg: .enable data tail/cp,tail/-sp,tail=tail / task=...tai asg=ti:6 // .disable data .close .return .; GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 675 Mass Ave, Cambridge, MA 02139, USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Library General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS Appendix: How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) 19yy This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) 19yy name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License.