title: Recursively list directory content, plus check if file is directory
body: <p>I'm trying to learn assembly, so bear with me if my problems are elementary</p>
  
  <p>The following code scans a directory and prints out all files and directories in it, except those starting with a dot. It seems to work fine.</p>
  
  <p>However, once I uncomment the <code>call scandir</code> line to turn on recursion, it prints out a long list of repeating filenames (see below for details). </p>
  
  <p>Also, I'd like a way to test if the file is a directory or not. How would I do that? As far as I can tell that isn't the problem, for now, since if it's not a directory the call to scandir will just return without doing anything, but the check might become important later on (and it seems a good thing to do anyway).</p>
  
  <pre><code>[SECTION .data]
  DirName     db 'test', 0
  
  [SECTION .bss]
  
  [SECTION .text]
  
  extern puts                  ; Externals from glibc standard C library
  extern opendir               ; Externals from dirent.h
  extern closedir
  extern readdir
  
  global main
  
  
  scandir:
      pushad                  ; Save caller's registers
  
      push eax                ; Directory is passed in eax
      call opendir            ; Open directory
      add esp, 4
      cmp eax, 0              ; opendir returns 0 on failure
      je .done
  
      mov ebx, eax            ; Move directory handle to ebx
  
      .read:
          push ebx            ; Push directory handle
          call readdir        ; Read a file from directory
          add esp, 4          ; Clean up the stack
          cmp eax, 0          ; readdir returns 0 on failure or when done
          je .close
  
          add eax, 11         ; File name is offset at 11 bytes
  
          mov cl, byte [eax]  ; Get first char of filename
          cmp cl, 46          ; Ignore files and dirs which begin with a dot
          je .read            ; (., .., and hidden files)
  
          ;call scandir       ; Call scandir recursively
                              ; If file is not a dir opendir will simply fail
  
          push eax
          call puts
          add esp, 4
  
          jmp .read
  
      .close:
          push ebx                ; Close directory
          call closedir
          add esp, 4
          jmp .done
  
      .done:
          popad                   ; Restore caller's registers
          ret
  
  main:
      push ebp                ; Set up stack frame for debugger
      mov ebp, esp
      push ebx                ; Must preserve ebp, ebx, esi and edi
      push esi
      push edi
      ; start
  
      mov eax, DirName
      call scandir
  
      ; end
      pop edi                 ; Restore saved registers
      pop esi
      pop ebx
      mov esp, ebp            ; Destroy stack frame
      pop ebp
      ret
  </code></pre>
  
  <p>The directory structure of the test directory is like this:</p>
  
  <pre><code>bar [directory]
      bas.c
  test1.c
  test2.c
  test3.c
  foo.txt
  test
  </code></pre>
  
  <p>Without recursion it prints out the files in the test directory as it should, but with recursion it seems to print the following:</p>
  
  <pre><code>test1.c
  bar
  test3.c
  [repeat 3 lines ~1000 times]
  test
  foo.txt
  test2.c
  [repeat 3 lines ~1000 times]
  </code></pre>
  
  <p><strong>Edit:</strong> This now mostly works, I think, except that it seems to jump back into the directory below 'test' initially, causing it to list the files there and the files in 'test' twice</p>
  
  <pre><code>[SECTION .data]
  ParentDir   db '..', 0
  CurrentDir  db '.', 0
  DirName     db 'test', 0
  
  [SECTION .bss]
  
  [SECTION .text]
  extern puts                 ; Externals from glibc standard C library
  extern opendir              ; Externals from dirent.h
  extern closedir
  extern readdir
  extern chdir
  
  global main
  
  scandir:
      pushad                  ; Save caller's registers
  
      push eax                ; Directory is passed in eax
      call opendir            ; Open directory
      add esp, 4
      cmp eax, 0              ; opendir returns 0 on failure
      je .done
  
      mov ebx, eax            ; Move directory handle to ebx
  
      .read:
          push ebx            ; Push directory handle
          call readdir        ; Read a file from directory
          add esp, 4          ; Clean up the stack
          cmp eax, 0          ; readdir returns 0 on failure or when done
          je .close
  
          add eax, 11         ; File name is offset at 11 bytes
  
          mov cl, byte [eax]  ; Get first char of filename
          cmp cl, 46          ; Ignore files and dirs which begin with a dot
          je .read            ; (., .., and hidden files)
  
          cmp byte [eax-1], 4
          jne .notdir
  
          push eax
          call chdir
          add esp, 4
          mov eax, CurrentDir
          call scandir        ; Call scandir recursively
          jmp .read
  
      .notdir:
          push eax
          call puts
          add esp, 4
  
          jmp .read
  
      .close:
          push ebx                ; Close directory
          call closedir
          add esp, 4
  
          push ParentDir
          call chdir
          add esp, 4
  
          jmp .done
  
      .done:
          popad                   ; Restore caller's registers
          ret
  
  main:
      push ebp                ; Set up stack frame for debugger
      mov ebp, esp
      push ebx                ; Must preserve ebp, ebx, esi and edi
      push esi
      push edi
      ; start
  
      mov eax, DirName
      call scandir
  
      ; end
      pop edi                 ; Restore saved registers
      pop esi
      pop ebx
      mov esp, ebp            ; Destroy stack frame
      pop ebp
      ret
  </code></pre>