The QBasic / QB64 Discussion Forum      Other Subforums, Links and Downloads
 
 Return to Index  

[QBasic|QuickBASIC] File and directory searching

December 4 2009 at 6:07 PM
Laanan Fisher  (no login)

For those that want an easy, robust and efficient way of retrieving file and directory names and attributes, check this out:


' Searches for the first file matching the file spec `filespec` (may include
' wildcards) that has at most the file attributes specfied in `attributes`. If
' `filespec` is an empty string, then the next such file is searched for. If
' no such file exists, an empty string is returned. Otherwise, the name of the
' file found is returned, and `attributes` then contains the actual file
' attributes of the file.
'
' NOTE: Files with no attributes set are always matched. Use the returned
' value of `attributes` to filter, for instance, files with *only* the
' read-only attribute set.
declare function Dir$ ( filespec as string, attributes as integer )

' File attribute flags
const fileReadOnly%     = &H01
const fileHidden%       = &H02
const fileSystem%       = &H04
const fileVolume%       = &H08
const fileDirectory%    = &H10
const fileArchive%      = &H20
const fileNormal%       = fileArchive or fileReadOnly or fileDirectory
const fileAny%          = fileNormal or fileHidden or fileSystem or fileVolume

        '' ::::: example usages
        dim attributes as integer
        dim filename as string
        
        ' list all of the files in the current directory that have a
        ' .BAS extension, and are either archives, read-only or neither:
        filename = Dir( "*.bas", attributes )
        do while "" <> filename
            print filename
            filename = Dir( "", attributes )
        loop
        
        ' check to see if the file "notafile.xyz" exists:
        if "" = Dir( "notafile.xyz", fileAny ) then
            print "file does not exist."
        end if



If there's some demand, I'll probably add the ability to retrieve time/date and size info as well. Let me know what you think, and if you have any problems with it. Here is the implementation:

function Dir$ ( filespec as string, attributes as integer )

    static mcode as string * 179

    static mcodeInitialized as integer

    if 0 = mcodeInitialized then

        mcodeInitialized = -1



        mid$( mcode, 1, 4 ) = chr$( 233 )+ chr$( 48 )+ chr$( 0 )+ chr$( 0 )

        mid$( mcode, 5, 4 ) = chr$( 0 )+ chr$( 0 )+ chr$( 0 )+ chr$( 0 )

        mid$( mcode, 9, 4 ) = chr$( 0 )+ chr$( 0 )+ chr$( 0 )+ chr$( 0 )

        mid$( mcode, 13, 4 ) = chr$( 0 )+ chr$( 0 )+ chr$( 0 )+ chr$( 0 )

        mid$( mcode, 17, 4 ) = chr$( 0 )+ chr$( 0 )+ chr$( 0 )+ chr$( 0 )

        mid$( mcode, 21, 4 ) = chr$( 0 )+ chr$( 0 )+ chr$( 0 )+ chr$( 0 )

        mid$( mcode, 25, 4 ) = chr$( 0 )+ chr$( 0 )+ chr$( 0 )+ chr$( 0 )

        mid$( mcode, 29, 4 ) = chr$( 0 )+ chr$( 0 )+ chr$( 0 )+ chr$( 0 )

        mid$( mcode, 33, 4 ) = chr$( 0 )+ chr$( 49 )+ chr$( 50 )+ chr$( 51 )

        mid$( mcode, 37, 4 ) = chr$( 52 )+ chr$( 53 )+ chr$( 54 )+ chr$( 55 )

        mid$( mcode, 41, 4 ) = chr$( 56 )+ chr$( 46 )+ chr$( 49 )+ chr$( 50 )

        mid$( mcode, 45, 4 ) = chr$( 51 )+ chr$( 0 )+ chr$( 0 )+ chr$( 0 )

        mid$( mcode, 49, 4 ) = chr$( 1 )+ chr$( 2 )+ chr$( 3 )+ chr$( 85 )

        mid$( mcode, 53, 4 ) = chr$( 137 )+ chr$( 229 )+ chr$( 139 )+ chr$( 94 )

        mid$( mcode, 57, 4 ) = chr$( 12 )+ chr$( 138 )+ chr$( 7 )+ chr$( 60 )

        mid$( mcode, 61, 4 ) = chr$( 0 )+ chr$( 116 )+ chr$( 82 )+ chr$( 139 )

        mid$( mcode, 65, 4 ) = chr$( 86 )+ chr$( 6 )+ chr$( 129 )+ chr$( 194 )

        mid$( mcode, 69, 4 ) = chr$( 3 )+ chr$( 0 )+ chr$( 184 )+ chr$( 0 )

        mid$( mcode, 73, 4 ) = chr$( 26 )+ chr$( 205 )+ chr$( 33 )+ chr$( 139 )

        mid$( mcode, 77, 4 ) = chr$( 94 )+ chr$( 10 )+ chr$( 139 )+ chr$( 15 )

        mid$( mcode, 81, 4 ) = chr$( 139 )+ chr$( 94 )+ chr$( 6 )+ chr$( 129 )

        mid$( mcode, 85, 4 ) = chr$( 195 )+ chr$( 46 )+ chr$( 0 )+ chr$( 137 )

        mid$( mcode, 89, 4 ) = chr$( 15 )+ chr$( 139 )+ chr$( 86 )+ chr$( 12 )

        mid$( mcode, 93, 4 ) = chr$( 185 )+ chr$( 255 )+ chr$( 0 )+ chr$( 184 )

        mid$( mcode, 97, 4 ) = chr$( 0 )+ chr$( 78 )+ chr$( 205 )+ chr$( 33 )

        mid$( mcode, 101, 4 ) = chr$( 114 )+ chr$( 75 )+ chr$( 139 )+ chr$( 94 )

        mid$( mcode, 105, 4 ) = chr$( 6 )+ chr$( 129 )+ chr$( 195 )+ chr$( 24 )

        mid$( mcode, 109, 4 ) = chr$( 0 )+ chr$( 138 )+ chr$( 7 )+ chr$( 180 )

        mid$( mcode, 113, 4 ) = chr$( 0 )+ chr$( 60 )+ chr$( 0 )+ chr$( 116 )

        mid$( mcode, 117, 4 ) = chr$( 38 )+ chr$( 139 )+ chr$( 94 )+ chr$( 6 )

        mid$( mcode, 121, 4 ) = chr$( 129 )+ chr$( 195 )+ chr$( 24 )+ chr$( 0 )

        mid$( mcode, 125, 4 ) = chr$( 138 )+ chr$( 7 )+ chr$( 180 )+ chr$( 0 )

        mid$( mcode, 129, 4 ) = chr$( 139 )+ chr$( 94 )+ chr$( 6 )+ chr$( 129 )

        mid$( mcode, 133, 4 ) = chr$( 195 )+ chr$( 46 )+ chr$( 0 )+ chr$( 139 )

        mid$( mcode, 137, 4 ) = chr$( 15 )+ chr$( 33 )+ chr$( 193 )+ chr$( 129 )

        mid$( mcode, 141, 4 ) = chr$( 249 )+ chr$( 0 )+ chr$( 0 )+ chr$( 117 )

        mid$( mcode, 145, 4 ) = chr$( 10 )+ chr$( 184 )+ chr$( 0 )+ chr$( 79 )

        mid$( mcode, 149, 4 ) = chr$( 205 )+ chr$( 33 )+ chr$( 114 )+ chr$( 25 )

        mid$( mcode, 153, 4 ) = chr$( 233 )+ chr$( 203 )+ chr$( 255 )+ chr$( 139 )

        mid$( mcode, 157, 4 ) = chr$( 94 )+ chr$( 10 )+ chr$( 137 )+ chr$( 7 )

        mid$( mcode, 161, 4 ) = chr$( 185 )+ chr$( 13 )+ chr$( 0 )+ chr$( 139 )

        mid$( mcode, 165, 4 ) = chr$( 118 )+ chr$( 6 )+ chr$( 129 )+ chr$( 198 )

        mid$( mcode, 169, 4 ) = chr$( 33 )+ chr$( 0 )+ chr$( 30 )+ chr$( 7 )

        mid$( mcode, 173, 4 ) = chr$( 139 )+ chr$( 126 )+ chr$( 14 )+ chr$( 243 )

        mid$( mcode, 177, 4 ) = chr$( 164 )+ chr$( 93 )+ chr$( 203 )

    end if
    
    dim filespecZ as string * 128 : filespecZ = filespec + chr$( 0 )
    dim isNext as integer         : isNext = (0 = len( filespec ))
    dim result as string * 13     : result = string$( 13, 0 )
    
    call absolute( byval varptr( result ), byval varptr( filespecZ ), attributes, byval isNext, byval varptr( mcode ), varptr( mcode ) )
    Dir$ = left$( result, instr( result, chr$( 0 ) ) - 1 )

end function


For those interested, here's the NASM source:

cpu 8086
org 0x0000

; stack:
;   [bp + 14] byval result    as integer  ; offset of QB string char data
;   [bp + 12] byval filespec  as integer  ; offset of ASCIIZ string
;   [bp + 10] attribs         as integer  ; offset of the attributes var
;   [bp +  8] byval isNext    as integer  ; zero if finding first file
;   [bp +  6] byval codeoff   as integer  ; offset of this code


; :::::
Data: jmp Code

    .DTA_reserved   resb    0x15
    .DTA_attribs    db      0x00
    .DTA_time       dw      0x0000
    .DTA_date       dw      0x0000
    .DTA_size       dd      0x00000000
    .DTA_filename   db      '12345678.123', 0
    
    .attribs        dw      0x0000
    .dummy db 1, 2, 3

; :::::
Code:

    push        bp
    mov         bp, sp
    
    mov         bx, [bp + 12]
    mov         al, [bx]
    cmp         al, 0x00
    je          FindNext
    
    ; tell DOS to store disk transfer results in our DTA
    mov         dx, [bp + 6]
    add         dx, Data.DTA_reserved
    mov         ax, 0x1A00
    int         0x21
    
    ; store search attributes
    mov         bx, [bp + 10]
    mov         cx, [bx]
    mov         bx, [bp + 6]
    add         bx, Data.attribs
    mov         [bx], cx
    
FindFirst:
    mov         dx, [bp + 12]       ; ds:dx = file spec
    mov         cx, 0x00FF          ; match any attribute combination
    mov         ax, 0x4E00
    int         0x21
    jc          FileNotFound

CheckFileAttributes:
    mov         bx, [bp + 6]
    add         bx, Data.DTA_attribs
    mov         al, [bx]
    mov         ah, 0x00
    cmp         al, 0x00            ; no attributes ? always a match
    je          FileFound
    ;
    mov         bx, [bp + 6]
    add         bx, Data.DTA_attribs
    mov         al, [bx]
    mov         ah, 0x00
    mov         bx, [bp + 6]
    add         bx, Data.attribs
    mov         cx, [bx]
    and         cx, ax
    cmp         cx, 0x0000          ; any of the specified attributes ?
    jne         FileFound

FindNext:
    mov         ax, 0x4F00
    int         0x21
    jc          FileNotFound
    jmp         CheckFileAttributes

FileFound:
    mov         bx, [bp + 10]       ; return found file attributes
    mov         [bx], ax            ; .
    
    mov         cx, 13                  ; copy entire DTA name field
                                        ; (will be truncated in QB code)
    mov         si, [bp + 6]            ; ds:si = DTA_filename
    add         si, Data.DTA_filename   ; .
    push        ds                      ; es:di = result char data
    pop         es                      ;  ...
    mov         di, [bp + 14]           ; .
rep movsb
    
FileNotFound:
    pop     bp
    retf

 
 Respond to this message   
Response TitleAuthor and Date
Oops, the example should look like: (View Thread)Laanan Fisher on Dec 4
I hate to INTERRUPT, but....... (View Thread) on Dec 5
   Re: I hate to INTERRUPT, but.......Laanan Fisher on Dec 5
      Here is a file attribute SUB that does not need the DTA TYPE on Dec 5
Recursive directory listing (View Thread)Laanan Fisher on Dec 5
Size and last access date and time (View Thread)Laanan Fisher on Dec 7
   Re: Size and last access date and timeLaanan Fisher on Dec 7
      * Suggestion: Why not post these in the ASM Forum? on Dec 7
         Re: * Suggestion: Why not post these in the ASM Forum?Laanan Fisher on Dec 7

Newbies usually go to www.qbasic.com and click on The QBasic Forum
Forum regulars have their own ways, which include The QBasic Community Forums