News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

My attempt at directory recursion.

Started by Ehtyar, October 03, 2006, 10:46:32 AM

Previous topic - Next topic

Ehtyar

Hi all.
I started a new project today, and sfv/md5/sha file generator, but thus far im not having much luck.
I borrowed the directory recursion code from Hutch's directory case converter, and after about 2 hours, i still havnt got it working properly. I apologise in advance for any really obvious screw-ups in here, but my brain is somewhat mushed at the moment, thanks to this un-cooperative code. If anyone could advise me on what i need to do to get this work, i'd very much appreciate it.
It should simply recurse through the directory structure below the executable itself, and print relative file paths/names to dir.txt.
Thanks, Ehtyar.
.586
.MODEL FLAT, STDCALL
option casemap:none

include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
include \masm32\include\masm32.inc

includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\masm32.lib

include \masm32\macros\macros.asm

.data
boro    db "dir.txt", 0
diry    db "*.*",0
.data?

handle  dd ?
fHand   dd ?
bytwrit dd ?

.code
start:
call main
exit

Find_Files proc hFile:DWORD, ppatn:DWORD
LOCAL hSrch :DWORD
LOCAL wfd   :WIN32_FIND_DATA
invoke FindFirstFile,ppatn,addr wfd
mov hSrch, eax
.IF hSrch != INVALID_HANDLE_VALUE
invoke szCmp, addr wfd.cFileName, chr$(".", 0)
cmp eax, 0
je @F
.IF wfd.dwFileAttributes == FILE_ATTRIBUTE_DIRECTORY
invoke SetCurrentDirectory, addr wfd.cFileName
invoke Find_Files,hFile,ppatn                 ; recurse to next directory level
.ENDIF
invoke szLen, addr wfd.cFileName
mov word ptr [wfd.cFileName+eax], 0a0dh
add eax, 2
invoke WriteFile, hFile, addr wfd.cFileName, eax, addr bytwrit, 0
@@:
invoke FindNextFile,hSrch,addr wfd
test eax, eax
jz close_file
invoke szCmp, addr wfd.cFileName, chr$("..", 0)
cmp eax, 0
je @F
.IF wfd.dwFileAttributes == FILE_ATTRIBUTE_DIRECTORY
invoke SetCurrentDirectory, addr wfd.cFileName
invoke Find_Files,hFile,ppatn                 ; recurse to next directory level
.ENDIF
invoke szLen, addr wfd.cFileName
mov word ptr [wfd.cFileName+eax], 0a0dh
add eax, 2
invoke WriteFile, hFile, addr wfd.cFileName, eax, addr bytwrit, 0
@@:                                     ; loop through the rest
invoke FindNextFile,hSrch,addr wfd
cmp eax, 0
je close_file
invoke szLen, addr wfd.cFileName
mov word ptr [wfd.cFileName+eax], 0a0dh
add eax, 2
invoke WriteFile, hFile, addr wfd.cFileName, eax, addr bytwrit, 0
.IF wfd.dwFileAttributes == FILE_ATTRIBUTE_DIRECTORY
invoke SetCurrentDirectory, addr wfd.cFileName
invoke Find_Files,hFile,ppatn                 ; recurse to next directory level
.ENDIF
jmp @B
close_file:
invoke FindClose,hSrch
.ENDIF
invoke SetCurrentDirectory, chr$("..", 0)                              ; drop back to next lower directory
ret
Find_Files endp


main proc
invoke DeleteFile, addr boro
invoke CreateFile, addr boro, GENERIC_WRITE, 0, 0, CREATE_NEW, 0, 0
mov fHand, eax
invoke Find_Files, fHand, addr diry
invoke CloseHandle, fHand
ret
main endp


end start

PBrennick

Ehtyar,
I wanted to test your code so I could help you but I was unable to compile the code because  the MBox macro cannot be found.  I will check if this is supposed to be in macros.asm in masm32.

Paul

The GeneSys Project is available from:
The Repository or My crappy website

Ehtyar

Oh nooooo, I'm so sorry. It's a cusom one, I use it for random debugging kinda thing, I'll edit my post to remove it :'( I knew one day adding functions to the standard macros file would come back to bite me in the a** :(

PBrennick

Ehtyar,
No problem, thanks for the quick reply and I will continue testing.  Just a word of caution, you should create your own macros file.  Don't ever modify macros.asm or windows.inc or everyone who wants to help you will be frustrated.

Paul
The GeneSys Project is available from:
The Repository or My crappy website

Tedd

First problem is that you're not recursing!
This is due to the line
.IF wfd.dwFileAttributes == FILE_ATTRIBUTE_DIRECTORY
which will only work if the attribute for the directory is ONLY directory (they may have others set).
So you need to 'test' instead of 'cmp' :wink
No snowflake in an avalanche feels responsible.

Tedd

Fixed and cleaned up :wink

It doesn't prefix the relative directories -- I've left the fun part for you :green2

Find_Files proc hFile:DWORD,ppatn:DWORD
    LOCAL hSrch :DWORD
    LOCAL wfd   :WIN32_FIND_DATA
    invoke FindFirstFile,ppatn,addr wfd
    mov hSrch, eax
    .IF hSrch != INVALID_HANDLE_VALUE
      @loopy:
        mov eax,wfd.dwFileAttributes
        test eax,FILE_ATTRIBUTE_DIRECTORY
        .IF (zero?)
            ;print the file name
            ;(we don't print directory names alone -- they should be prefixed to the files)
            invoke szLen, addr wfd.cFileName
            mov word ptr [wfd.cFileName+eax], 0a0dh
            add eax, 2
            invoke WriteFile, hFile,addr wfd.cFileName, eax, addr bytwrit, 0
        .ELSE
            ;skip "." and ".." 'directories'
            mov eax,DWORD PTR [wfd.cFileName]
            cmp eax,002eh   ;"."
            je @next
            cmp eax,002e2eh ;".."
            je @next
            ;recurse to next directory level
            invoke SetCurrentDirectory, addr wfd.cFileName
            invoke Find_Files,hFile,ppatn
        .ENDIF
      @next:
        invoke FindNextFile,hSrch,addr wfd
        cmp eax, 0
        jne @loopy

        invoke FindClose,hSrch
    .ENDIF
    invoke SetCurrentDirectory, chr$("..", 0)   ; drop back to next lower directory
    ret
Find_Files endp


This SHOULD work -- and it does in a debugger, but not if you run it directly :dazzled:
Not quite sure what's happening, but I have a feeling that the changing current directory is messing up the 'state' of the FindFirstFile handles.
No snowflake in an avalanche feels responsible.

Ehtyar

Thanks so much for the help guys and i'm so sorry for the initial screw up Paul. I never realised directory recursion was so damn complicated. I guess this is why i found only one example elsewhere that apparently dosnt work anyway :(
Wow Tedd, you rewrote half the code, thank you!!! You are indeed correct , works in a debugger but not elsewhere, and as you may have guessed debugging is NOT my strong suit, nor is directory recursion apparently. I must admit that i'm surprised there isnt more source available for this kind of thing, can't imagine i'm the first person who wanted this.
I guess unless we get it working somehow i'll have to skip the directory recursion bit and just do a single directory. When i finish the project though ill post the source and anyone can have a play with it, but it will be basic i think, maybe not even a dialog...
Thanks again for the help guys, least i picked up a few more tricks from the pros today ;) I'll keep on it for a little while longer but to be honest im about up to my neck in this code. Source will be posted soon, thanks again.
Ehtyar

PBrennick

Ehtyar,
There is code for this that works.  Hutch wrote a program that deletes certain files in the entire tree from the initial directory on down.  I can't quite remember what it was but I will find out for you so hold on, don't give up yet.

Paul
The GeneSys Project is available from:
The Repository or My crappy website

PBrennick

Ehtyar,
By the way, I ran the program using OllyDbg and WinDbg and they both ran it differently for some reason. Olly does the bore down starting from the location of Recursion.exe (this is the way it should work).  WinDbg, though, does the bore down starting from the location of WinDbg.exe.  This is unusual in my opinion.  It is probably because WinDbg is not working correctly, so you should not use it.

Paul
The GeneSys Project is available from:
The Repository or My crappy website

PBrennick

Ehtyar,
Here is the program that does directory recursion as I mentioned in an earlier post.



comment *
     -------------------------------------------------
    | This utility, a CONSOLE application, recurses   |
    | the directory tree from its current location    |
    | and changes the case of all filenames to lower. |
     -------------------------------------------------
*
    .386
    .model  flat,stdcall
    option  casemap:none

    include \masm32\include\windows.inc
    include \masm32\macros\macros.asm

    uselib MACRO namelist:VARARG
    FOR item, <namelist>
      include \masm32\include\item.inc
      includelib \masm32\lib\item.lib
      ENDM
    ENDM

    uselib kernel32,masm32

    Find_Files  PROTO :DWORD
    change_case PROTO :DWORD

.code
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
start:
    call    main
    exit

main    proc
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    fn  Find_Files,"*.*"
    ret
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
main    endp

Find_Files  proc ppatn:DWORD
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    LOCAL   hSrch:DWORD
    LOCAL   wfd:WIN32_FIND_DATA
    LOCAL   pbuf:DWORD
    LOCAL   buffer[260]:BYTE

    mov     pbuf, ptr$(buffer)
    mov     hSrch, rv(FindFirstFile, ppatn, ADDR wfd)
    invoke  SetFileAttributes, ADDR wfd.cFileName, FILE_ATTRIBUTE_NORMAL
    .if hSrch != INVALID_HANDLE_VALUE
      lea     eax, wfd.cFileName
      switch$ eax
        case$   "."                     ; Bypass current directory character
          jmp     @F
      endsw$
      .if wfd.dwFileAttributes == FILE_ATTRIBUTE_DIRECTORY
        chdir   ADDR wfd.cFileName
        fn      Find_Files, ppatn       ; Recurse to the next directory level
      .endif
      invoke  change_case, ADDR wfd.cFileName
@@:
      test    rv(FindNextFile, hSrch, ADDR wfd), eax
      jz      close_file
      invoke  SetFileAttributes, ADDR wfd.cFileName, FILE_ATTRIBUTE_NORMAL
      lea     eax, wfd.cFileName
      switch$ eax
        case$   ".."                    ; Bypass previous directory characters
          jmp     @F
      endsw$
      .if wfd.dwFileAttributes == FILE_ATTRIBUTE_DIRECTORY
        chdir   ADDR wfd.cFileName
        fn      Find_Files, ppatn       ; Recurse to next directory level
      .endif
      invoke  change_case, ADDR wfd.cFileName
@@:                                     ; Loop through the rest
      test    rv(FindNextFile, hSrch, ADDR wfd), eax
      jz      close_file
      invoke  SetFileAttributes, ADDR wfd.cFileName, FILE_ATTRIBUTE_NORMAL
      invoke  change_case, ADDR wfd.cFileName
      .if wfd.dwFileAttributes == FILE_ATTRIBUTE_DIRECTORY
        chdir   ADDR wfd.cFileName
        fn      Find_Files, ppatn       ; Recurse to next directory level
      .endif
      jmp     @B
close_file:
      invoke  FindClose,hSrch
    .endif
    chdir   ".."                        ; Drop back to next lower directory
    ret
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Find_Files  endp

change_case proc fname:DWORD
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    LOCAL   pbuf:DWORD
    LOCAL   buffer[260]:BYTE

    mov     pbuf, ptr$(buffer)

;   Rename file to lower case form
    invoke  MoveFile, fname, lcase$(fname)


    invoke  GetCurrentDirectory, 260, pbuf
    print   lcase$(pbuf), "\"
    print   lcase$(fname), 13, 10       ; Display the lower case name
    ret
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
change_case endp

    end     start


I hope this helps you.
Paul
The GeneSys Project is available from:
The Repository or My crappy website

PBrennick

Ehtyar,
To me, it looks like your modification to add the filename to the proc is what is screwing it up.  Leave the proc as is and call it with just the pattern as Hutch is doing and then add the stuff that writes to dir.txt into the proc instead of passing it from outside.

Do you understand my point?

Also, the program works correctly if it is run from a folder that does not have any directory entries.

Paul
The GeneSys Project is available from:
The Repository or My crappy website

Shantanu Gadgil

#11
To Ehtyar,
Nice to see someone else too doing directory recursion. Just a suggestion though, don't use the SetCurrentDirectory or whatever equivalent (chdir, et al) calls at all. Recusrion can be achieved by just using strings :) :) and no actual calls to set directory are required!!!

I will just run you through the algo, leaving the coding upto you!  :wink :wink
Starting with some initial path, find all files (in a loop)
If it is directory (dir attrib is SET) and the directory is NOT "." or ".." then concatenate the directory name to the current directory and run the same loop again!
In case you want to run some "logic" on the files (change time/attrib etc) you have found, you can stick it in the else part of the if directory.

At the place you "went into" a directory (by concatenating the strings) you would also need to "go parent" (chop off the directory name you just "came out of").

I hope so much helps.

OR

You could check the source here:  :bg
http://www.masm32.com/board/index.php?topic=3026.msg32540#msg32540

Regards,
Shantanu
To ret is human, to jmp divine!

drizz

here is an example program i wrote, it searches "c:\program files" for "*.mp3"
directory is printed in the console title and file fiound to stdout.
it takes a callback in arguments so you can modify it to do something else easily.

[attachment deleted by admin]
The truth cannot be learned ... it can only be recognized.

Ehtyar

Well i am officially about to loose my mind. This is even more difficult to implement than CreateProcess (don't ask).
I just spent about another 2 hours working on this, using the example from drizz (good thing it's not working for me, i'm using your hashing routines aswell, might aswell be coded 100% by you :P) and from Tedd. I have concluded that this is entirely too much work for something that will most likely not be used. Fortunately, your examples have given me more insight into a lot of things that are going to help me a lot with this project. I'm going to make a start on it now, without the directory recursion. With any luck i'll have something by tonight or maybe tomorrow (i'm a little obsessive ;))
Generally im too pu**y to release my source code for fear i've made some stupid mistake and everyone will think i'm a dumba**. I guess that has already been accomplished today, so i thought what the hell. My first attempt, WITH source, will be posted tomorrow, thanks again for your help guys, and hope this thing is useful to you somehow.

Thankfully, Ehtyar.

hutch--

I am not sure if I posted this one while I was playing with recursive directory code but it is basically very simple and as it only displays file names, its safe to play with without trashing anything.

I remember with some humour that one of the examples I was playing with on a spare partition that did recursive delete took everything out including the code for the recursive app. Only the EXE was left and it took out the asm file as well.  :red

[attachment deleted by admin]
Download site for MASM32      New MASM Forum
https://masm32.com          https://masm32.com/board/index.php