'=========================================================================== ' Subject: TEXT FILE VIEWER Date: 01-30-97 (14:40) ' Author: Craig Wright Code: QB, QBasic, PDS ' Origin: e-mail anonymous Packet: TEXT.ABC '=========================================================================== 'Description ' QBasic simple plain ASCii text file viewer for _BIG_ files ' Theoretical max file size (using QBasic) is about 16000 x 64kb ' or 1 gb, using typical real-world files reduces this to 512 mb ' It was coded in response to a request from a beginner to show how ' to view big files in QBasic and is _ONLY_ a shell <250 lines, ' you can add whatever you want ; left/right scroll, mouse etc. ' This is not claimed to be the ideal solution just another method. ' I may have gone a bit overboard on the comments :) 'Pro's and Cons ' Note that 16383 and 65535 have been rounded down. ' It's slow when scrolling from each 64kb chunk although this can ' be reduced in size to make loading quicker. Lots of wasteage due to ' file white space/overlap, even worse if long lines >80 need to be ' viewed. ' Using Pointer&(1000) and Text(64000 \ 80) requires 68kb and ' allows about 800 000 lines. ' The memory overhead can be reduced - Pointer&(2000) and ' Text(32000 \ 80) requires 40kb and allows 800 000 lines ! ' This is a best case, typical text files will not fill all of Text(), ' assuming 50% wasteage in file white space and overlap still allows ' 400 000 lines. ' For small files a file pointer array and SEEK to beginning of each ' line may be more appropriate but you MUST be using a cached drive ' and a hard disk, using this method with uncached drives or floppy, ' ZiP drives etc. will be very slow. ' Using Pointer1&(16000) and Pointer2&(16000) requires 128kb and ' allows 32 000 lines. ' 16000 &longs = 64kb = max QBasic array size 'Notes ' Instead of allocating an extra buffer (and associated code) ' when scrolling down from one 64kb chunk of file to another, the ' last bit of the previous 64kb chunk is used - overlap. ' Code broken up to avoid word wrapping when >80 characters 'Limits ' Only handles 80 character line lengths ' May be a noticeable delay for large files when loading in file ' chunks unless a disk cache such as SMARTDRV is used 'Bugs ' Page Up/Dn needs sorting ' Problem with displaying last file chunk if file>64kb, it'll ' display but will also move up more than 1 line '*************************************************************** '$DYNAMIC DEFINT A-Z DECLARE SUB BuildTable () DECLARE SUB CheckLimits () DECLARE SUB FindString (SearchFor$) DECLARE SUB GetFileChunk () DECLARE SUB GetFileToView () DECLARE FUNCTION GetKey$ () DECLARE SUB ResetScreen () DECLARE SUB ViewFile () 'top and bottom rows of text mode view limits CONST TopRow = 2 CONST BottomRow = 24 'back and foreground colours valid values are 1-15 CONST BackColour = 1 CONST ForeColour = 7 'transition buffer size/overlap (in lines) when loading 'from one 64kb chunk of file to another CONST TransitionLines = BottomRow - TopRow + 1 'Allocate 64kb to hold all or part of file, if using in your 'own code then set it to your preferred value and use REDIM 'to de-allocate memory. Note that reducing its size will also 'reduce delay between loading in each file chunk. '64kb = maximum size in Qbasic. 'QuickBasic/PDS/VB for DOS users can increase this size DIM SHARED Text(64000 \ 80) AS STRING * 80 'file position where 64kb chunks read from, max file size 'using 1000 is about 1000 x 64kb, increase 1000 to view 'larger files DIM SHARED Pointer(1000) AS LONG 'points to current 64kb chunk=Text() of file being viewed DIM SHARED CurrentFilePosition AS INTEGER 'total number of 64kb chunks in file DIM SHARED TotalChunks AS INTEGER, FileName$, FileHandle AS INTEGER DIM SHARED MaxLines AS INTEGER, LinesInText AS LONG, CurrentLine KEY OFF VIEW PRINT 'Kludge - increase typematic/scroll rate, only works if 'MS DOS v6.xx MODE.COM available in the path and CMOS not 'disabled it, different syntax required for other DOS's 'Makes big difference to scroll rate if typematic rate is slow 'Win v3.xx automatically increases typematic rate in DOS boxes 'For QB/PDS/VB use a call interrupt SHELL "mode con rate=32 delay=1" CLS 'get total lines in array buffer LinesInText = UBOUND(Text) 'set to 1st 64kb chunk in file CurrentFilePosition = 1 GetFileToView BuildTable 'load in first 64kb chunk GetFileChunk ViewFile ResetScreen END '************* error handlers BadPath: ResetScreen PRINT "Error ! unable to access your selected file -" PRINT " "; FileName$ PRINT PRINT "An invalid path, drive or file name may have been used": END BadErase: ResetScreen PRINT "Error ! unable to access disk": END InitializeError: ResetScreen PRINT "Error ! unknown problem occurred when loading in file": END ProgramError: ResetScreen PRINT "Error ! internal program error occurred": END REM $STATIC SUB BuildTable 'builds the pointer table for byte position in file 'where each 64kb chunk is to be loaded from ON ERROR GOTO InitializeError PRINT 'check if valid text file by getting line length of first 'line, should really check all lines but this may be too 'slow for big files LINE INPUT #FileHandle, a$ IF LEN(a$) > 80 THEN PRINT "Warning ! file contains lines that exceed 80 characters" PRINT "in length, it may not be a valid plain ASCii text file" PRINT PRINT "Press a key to continue or Q to quit" PRINT z$ = UCASE$(INPUT$(1)) IF z$ = "Q" THEN END END IF PRINT : PRINT "Please wait - loading in file" SEEK #FileHandle, 1 'first 64kb chunk of file = 1 TotalChunks = 1 'first chunk in file always starts at byte 1 Pointer(TotalChunks) = 1 DO WHILE NOT EOF(FileHandle) LINE INPUT #FileHandle, a$ i = i + 1 IF i > LinesInText - TransitionLines THEN 'finished counting this 64kb chunk TotalChunks = TotalChunks + 1 Pointer(TotalChunks) = SEEK(FileHandle) 'check exceeds table array size IF TotalChunks = UBOUND(Pointer) THEN EXIT DO 'show table being built, only needed for 'large files and slow systems PRINT "±"; i = 0 END IF LOOP CLS END SUB SUB CheckLimits 'moving up through file IF CurrentLine < 1 THEN CurrentLine = 1 'if file is >64kb then load in previous 64 kb chunk IF TotalChunks > 1 THEN IF CurrentFilePosition > 1 THEN 'not at file beginning so load in previous '64 kb chunk of file CurrentFilePosition = CurrentFilePosition - 1 GetFileChunk CurrentLine = MaxLines - TransitionLines + 1 END IF END IF END IF 'moving down through file IF CurrentLine > MaxLines - TransitionLines + 1 THEN CurrentLine = MaxLines - TransitionLines + 1 IF TotalChunks > 1 THEN IF CurrentFilePosition < TotalChunks THEN 'not at file end CurrentFilePosition = CurrentFilePosition + 1 GetFileChunk CurrentLine = 1 END IF END IF END IF IF CurrentLine < 1 THEN CurrentLine = 1 IF CurrentLine > MaxLines THEN CurrentLine = MaxLines END SUB SUB FindString (SearchFor$) 'gotta have something to search for IF LEN(SearchFor$) < 1 THEN EXIT SUB 'get original line and file position LastLine = CurrentLine LastPosition = CurrentFilePosition 'make it case insensitive SearchFor$ = LCASE$(SearchFor$) LOCATE 1, 2, 0: PRINT "Searching " DO 'show search in progress LOCATE 1, 13: PRINT USING "####"; CurrentLine CurrentLine = CurrentLine + 1 'check if need to load in next bit of file IF CurrentFilePosition < TotalChunks THEN CheckLimits IF INSTR(1, LCASE$(Text(CurrentLine)), SearchFor$) > 0 THEN EXIT SUB 'quit if last file chunk and last line in file IF CurrentLine >= MaxLines THEN EXIT DO LOOP 'nothing found so reset to original position in file CurrentLine = LastLine CurrentFilePosition = LastPosition GetFileChunk CheckLimits LOCATE 1, 2: PRINT "No matches found !" BEEP z$ = GetKey END SUB SUB GetFileChunk 'loads in a 64kb chunk of the file at a time 'make sure buffer is blank in case at end of file FOR i = 1 TO LinesInText Text(i) = "" NEXT 'go to file position where 64kb chunk to be loaded from SEEK #FileHandle, Pointer(CurrentFilePosition) MaxLines = 0 'FOR loop not used as there may not be enough lines 'to fill the buffer DO WHILE NOT EOF(FileHandle) LINE INPUT #FileHandle, a$ MaxLines = MaxLines + 1 'Strip out form feeds - needs more checking DO i = INSTR(a$, CHR$(12)) IF i > 0 THEN MID$(a$, i, 1) = " " LOOP UNTIL i = 0 'strip out TABs DO i = INSTR(a$, CHR$(9)) IF i > 0 THEN MID$(a$, i, 1) = " " LOOP UNTIL i = 0 Text(MaxLines) = a$ 'finished loading in this chunk IF MaxLines = LinesInText THEN EXIT DO LOOP END SUB SUB GetFileToView 'get file to view and check it exists PRINT : INPUT "Enter file to view : ", FileName$ IF LEN(FileName$) < 1 THEN PRINT : PRINT "Error ! no file selected": END END IF 'check file exists - trap invalid path, drive and file names ON ERROR GOTO BadPath FileHandle = FREEFILE OPEN FileName$ FOR BINARY AS #FileHandle 'Open in binary mode will create a new file if none 'exists, this is 0 bytes long - existing files are 'usually >0 bytes long IF LOF(FileHandle) = 0 THEN CLOSE FileHandle PRINT "Error ! your selected file does not exist !" PRINT 'clean up by erasing the new, zero length file ON ERROR GOTO BadErase KILL FileName$ END END IF CLOSE FileHandle 'file actually exists so open it in text mode FileHandle = FREEFILE OPEN FileName$ FOR INPUT AS #FileHandle END SUB FUNCTION GetKey$ 'returns a key press DO z$ = INKEY$ LOCATE 1, 72: PRINT TIME$ LOOP UNTIL z$ > "" GetKey$ = UCASE$(z$) END FUNCTION SUB ResetScreen 'close all open files, reset colours, clear screen before 'exit and switch on cursor CLOSE : COLOR 7, 0: CLS : LOCATE , , 0: PRINT END SUB SUB ViewFile 'initial start position in array buffer CurrentLine = 1 'total lines displayed on the screen LinesDisplayed = BottomRow - TopRow 'draw border & useful info COLOR 0, 7 LOCATE 1, 1: PRINT SPACE$(80); LOCATE 25, 1: PRINT SPACE$(80); LOCATE 1, 35: PRINT "Press H for Help - ESCape quits"; LOCATE 25, 2: PRINT "File : "; LEFT$(FileName$, 50); LOCATE 25, 60: PRINT "Size : "; LOF(FileHandle) \ 1000; "kb"; 'main view loop ON ERROR GOTO ProgramError DO 'show info about buffer COLOR 0, 7 LOCATE 1, 2, 0: PRINT "Chunk No/Line :"; PRINT CurrentFilePosition; "/"; CurrentLine; " " COLOR ForeColour, BackColour 'display the file - as Text() is fixed length string 'it will erase previously displayed lines - no erase 'line routine or padding out to end of line required FOR i = 0 TO LinesDisplayed LOCATE TopRow + i, 1: PRINT Text(CurrentLine + i); NEXT SELECT CASE GetKey CASE CHR$(0) + CHR$(72) 'Up CurrentLine = CurrentLine - 1 CASE CHR$(0) + CHR$(80) 'Down CurrentLine = CurrentLine + 1 CASE CHR$(0) + CHR$(73) 'Page Up CurrentLine = CurrentLine - LinesDisplayed CASE CHR$(0) + CHR$(81) 'Page Down CurrentLine = CurrentLine + LinesDisplayed CASE CHR$(0) + CHR$(71) 'Home CurrentLine = 0 'force update IF TotalChunks > 1 THEN CurrentFilePosition = 2 CheckLimits CurrentLine = 1 CASE CHR$(0) + CHR$(79) 'Endkey CurrentLine = MaxLines - TransitionLines + 2 CurrentFilePosition = TotalChunks - 1 CheckLimits CurrentLine = MaxLines - TransitionLines + 1 CASE CHR$(13), CHR$(27) 'Return or ESCape key exits main loop EXIT DO CASE "F" COLOR 0, 7: LOCATE 1, 1: PRINT SPACE$(80); COLOR 7, 0 LOCATE 1, 2, 1: INPUT "Enter text to search for : ", SearchFor$ COLOR 0, 7: LOCATE 1, 1: PRINT SPACE$(80); FindString (SearchFor$) COLOR 0, 7: LOCATE 1, 1: PRINT SPACE$(80); CASE "R" 'repeat last find FindString (SearchFor$) COLOR 0, 7: LOCATE 1, 1: PRINT SPACE$(80); CASE "H" COLOR 7, 0 FOR i = 7 TO 18 'clear partial screen LOCATE i, 9: PRINT SPACE$(60) NEXT LOCATE 8, 11: PRINT "Help" LOCATE 9, 13: PRINT "Up/Down - move" LOCATE 10, 13: PRINT "PageUp/Dn - Page Up and Page Down" LOCATE 11, 13: PRINT "Home - go to file beginning" LOCATE 12, 13: PRINT "End - go to file end" LOCATE 13, 13: PRINT "CR/ESCape - quit" LOCATE 14, 13: PRINT "F - find text string, case insensitive" LOCATE 15, 13: PRINT "R - repeat last find" LOCATE 16, 13: PRINT "H - help !" z$ = GetKey END SELECT 'bit inefficient this - calls it twice CheckLimits LOOP END SUB