PHP-Scandir for PureBasic

Share your advanced PureBasic knowledge/code with the community.
User avatar
Hroudtwolf
Addict
Addict
Posts: 803
Joined: Sat Feb 12, 2005 3:35 am
Location: Germany(Hessen)
Contact:

PHP-Scandir for PureBasic

Post by Hroudtwolf »

Hello,

I love the PHP command "Scandir".
Here ist the PureBasic-Variation of it.

Code: Select all

Procedure Scandir(*arraypointer,DriveOrPath.s,Pattern.s)
 Dim Scandir_temparray.s(0):Scandir_temparray() = *arraypointer
 FieldsOnArray.l=PeekL(@Scandir_temparray() - 8)
 DirID.l=ExamineDirectory(#PB_Any, DriveOrPath.s, Pattern.s)
 If DirID.l
   For Scandir_x.l=0 To FieldsOnArray.l-1
      CurDirEntry.l=NextDirectoryEntry()
      If CurDirEntry.l=1
         Scandir_temparray(Scandir_x.l)=DirectoryEntryName()
         ElseIf CurDirEntry.l=2
         Scandir_temparray(Scandir_x.l)="["+DirectoryEntryName()+"]"
      EndIf 
   Next Scandir_x
 EndIf 
 ProcedureReturn @Scandir_temparray() 
EndProcedure
Example:

Code: Select all

Dim MyDirectory.s(20)
MyDirectory()=Scandir(@MyDirectory(),"g:\","*.*")
For x=1 To 20
   Debug MyDirectory(x)
Next x
ebs
Enthusiast
Enthusiast
Posts: 561
Joined: Fri Apr 25, 2003 11:08 pm

Post by ebs »

Hroudtwolf,

Thank you! That's very handy.

Since you are passing a pointer to the array, you can simplify your code like this:

Code: Select all

Procedure Scandir(*arraypointer,DriveOrPath.s,Pattern.s) 
 Dim Scandir_temparray.s(0):Scandir_temparray() = *arraypointer 
 FieldsOnArray.l=PeekL(@Scandir_temparray() - 8) 
 DirID.l=ExamineDirectory(#PB_Any, DriveOrPath.s, Pattern.s) 
 If DirID.l 
   For Scandir_x.l=0 To FieldsOnArray.l-1 
      CurDirEntry.l=NextDirectoryEntry() 
      If CurDirEntry.l=1 
         Scandir_temparray(Scandir_x.l)=DirectoryEntryName() 
         ElseIf CurDirEntry.l=2 
         Scandir_temparray(Scandir_x.l)="["+DirectoryEntryName()+"]" 
      EndIf 
   Next Scandir_x 
 EndIf 
 ;ProcedureReturn @Scandir_temparray()  ; DON'T NEED THIS LINE
EndProcedure

Dim MyDirectory.s(20) 
Scandir(MyDirectory(),"g:\","*.*")      ; CHANGED THIS LINE 
For x=1 To 20 
   Debug MyDirectory(x) 
Next x
Regards,
Eric
benny
Enthusiast
Enthusiast
Posts: 465
Joined: Fri Apr 25, 2003 7:44 pm
Location: end of www
Contact:

Post by benny »

@Hroudti:

Nice code. PHP has some nice functions indeed - so it is always cool to see if
someone ports certain functions to PB.

However, I think it would be even cooler if you could pass to your function a
pointer to a linkedlist. So that you can get all entries of a directory without
knowing the total number of entries.

Unfortunately PB doesn't support it so far :?:

This could be a workaround - although I think it is not very handy :!:

Code: Select all

Structure Scandir_ListStruc
*Next.Scandir_ListStruc
*Previous.Scandir_ListStruc
Value.s
EndStructure

NewList MyDirectory.Scandir_ListStruc()

Procedure Scandir(*pList.Scandir_ListStruc,DriveOrPath.s,Pattern.s)
  DirID.l=ExamineDirectory(#PB_Any, DriveOrPath.s, Pattern.s) 
  If DirID.l   
    Repeat
      CurDirEntry.l=NextDirectoryEntry() 
      If CurDirEntry.l=1  
        *pList = AddElement(MyDirectory())
        *pList\Value = DirectoryEntryName() 
      ElseIf CurDirEntry.l=2
        *pList = AddElement(MyDirectory())
        *pList\Value = "["+DirectoryEntryName()+"]"
      EndIf
    Until CurDirEntry = 0
  EndIf 
EndProcedure


*pList.Scandir_ListStruc

Scandir(*pList,"c:\Windows\","*.*")

;Alphabetically sort
SortStructuredList(MyDirectory(), 2, OffsetOf(String\s), #PB_Sort_String)
;Print resultset
*pList = FirstElement(MyDirectory())
While *pList
  Debug *pList\Value
  *pList = *pList\Next
Wend
regards,
benny!
-
pe0ple ar3 str4nge!!!
DarkDragon
Addict
Addict
Posts: 2345
Joined: Mon Jun 02, 2003 9:16 am
Location: Germany
Contact:

Post by DarkDragon »

benny wrote:Unfortunately PB doesn't support it so far :?:
It does:

Code: Select all

Procedure CopyMemoryAMD(*src, *dst, size)
  #CACHEBLOCK = $80
  #CACHEBLOCKPREFETCH = #CACHEBLOCK/2
  #CACHEBLOCKTOP = #CACHEBLOCK*64
  #UNCACHED_COPY = 197*1024
  #UNCACHED_COPYPREFETCH = #UNCACHED_COPY/64
  #TINY_BLOCK_COPY = 64
  #IN_CACHE_COPY = 64*1024
  #IN_CACHE_COPYBIG = #IN_CACHE_COPY/64
  !MOV esi,dword [esp]
  !MOV edi,dword [esp+4]
  !MOV ecx,dword [esp+8]
  !MOV ebx,ecx
  !CLD
  !CMP ecx, 64
  !JB l_memcpy_ic_3
  !CMP ecx, 32*1024
  !JBE l_memcpy_do_align
  !CMP ecx, 64*1024
  !JBE l_memcpy_align_done
  memcpy_do_align:
  !MOV ecx,8
  !SUB ecx,edi
  !And ecx,7
  !SUB  ebx, ecx  ; update copy count
  !NEG  ecx    ; set up to jump into the array
  !ADD  ecx, l_memcpy_align_done
  !JMP  ecx    ; jump to array of movsb's
  !ALIGN 4
  !movsb
  !movsb
  !movsb
  !movsb
  !movsb
  !movsb
  !movsb
  !movsb
  memcpy_align_done:   ; destination is dword aligned
  !MOV  ecx, ebx  ; number of bytes left to copy
  !SHR  ecx, 6   ; get 64-byte block count
  !JZ  l_memcpy_ic_2 ; finish the last few bytes
  !CMP  ecx, 1024 ; too big 4 cache? use uncached copy
  !JAE  l_memcpy_uc_test
  ;!ALIGN 16
  memcpy_ic_1:   ; 64-byte block copies, in-cache copy
  !prefetchnta [esi+(200*64/34+192)]  ; start reading ahead
  !movq mm0, [esi+0] ; read 64 bits
  !movq mm1, [esi+8]
  !movq [edi+0], mm0 ; write 64 bits
  !movq [edi+8], mm1 ;    note:  the normal !movq writes the
  !movq mm2, [esi+16] ;    data to cache; a cache line will be
  !movq mm3, [esi+24] ;    allocated as needed, to store the data
  !movq [edi+16], mm2
  !movq [edi+24], mm3
  !movq mm0, [esi+32]
  !movq mm1, [esi+40]
  !movq [edi+32], mm0
  !movq [edi+40], mm1
  !movq mm2, [esi+48]
  !movq mm3, [esi+56]
  !movq [edi+48], mm2
  !movq [edi+56], mm3
  !ADD  esi, 64   ; update source pointer
  !ADD  edi, 64   ; update destination pointer
  !DEC  ecx    ; count down
  !JNZ  l_memcpy_ic_1 ; last 64-byte block?
  memcpy_ic_2:
  !MOV  ecx, ebx  ; has valid low 6 bits of the byte count
  memcpy_ic_3:
  !SHR  ecx, 2   ; dword count
  !And  ecx, 15 ; %1111  ; only look at the "remainder" bits
  !NEG  ecx    ; set up to jump into the array
  !ADD  ecx, l_memcpy_last_few
  !JMP  ecx    ; jump to array of movsd's
  memcpy_uc_test:
  !CMP  ecx, 3152 ; big enough? use block prefetch copy
  !JAE  l_memcpy_bp_1
  memcpy_64_test:
  !Or  ecx, ecx  ; tail end of block prefetch will jump here
  !JZ  l_memcpy_ic_2 ; no more 64-byte blocks left
  memcpy_uc_1:    ; 64-byte blocks, uncached copy
  !prefetchnta [esi+(200*64/34+192)]  ; start reading ahead
  !movq mm0, [esi+0]  ; read 64 bits
  !ADD  edi, 64   ; update destination pointer
  !movq mm1, [esi+8]
  !ADD  esi, 64   ; update source pointer
  !movq mm2, [esi-48]
  !movntq [edi-64], mm0 ; write 64 bits, bypassing the cache
  !movq mm0, [esi-40] ;    note: !movntq also prevents the CPU
  !movntq [edi-56], mm1 ;    from READING the destination address
  !movq mm1, [esi-32] ;    into the cache, only to be over-written
  !movntq [edi-48], mm2 ;    so that also helps performance
  !movq mm2, [esi-24]
  !movntq [edi-40], mm0
  !movq mm0, [esi-16]
  !movntq [edi-32], mm1
  !movq mm1, [esi-8]
  !movntq [edi-24], mm2
  !movntq [edi-16], mm0
  !DEC  ecx
  !movntq [edi-8], mm1
  !JNZ  l_memcpy_uc_1 ; last 64-byte block?
  !JMP  l_memcpy_ic_2  ; almost done
  memcpy_bp_1:   ; large blocks, block prefetch copy
  !CMP  ecx, 128   ; big enough to run another prefetch loop?
  !JL  l_memcpy_64_test   ; no, back to regular uncached copy
  !MOV  eax, 64  ; block prefetch loop, unrolled 2X
  !ADD  esi, 8192 ; move to the top of the block
  ;!ALIGN 16
  memcpy_bp_2:
  !MOV  edx, [esi-64]  ; grab one address per cache line
  !MOV  edx, [esi-128]  ; grab one address per cache line
  !SUB  esi, 128   ; go reverse order
  !DEC  eax     ; count down the cache lines
  !JNZ  l_memcpy_bp_2  ; keep grabbing more lines into cache
  !MOV  eax, 128  ; now that it's in cache, do the copy
  ;!ALIGN 16
  memcpy_bp_3:
  !movq mm0, [esi]  ; read 64 bits
  !movq mm1, [esi+ 8]
  !movq mm2, [esi+16]
  !movq mm3, [esi+24]
  !movq mm4, [esi+32]
  !movq mm5, [esi+40]
  !movq mm6, [esi+48]
  !movq mm7, [esi+56]
  !ADD  esi, 64    ; update source pointer
  !movntq [edi], mm0  ; write 64 bits, bypassing cache
  !movntq [edi+ 8], mm1  ;    note: !movntq also prevents the CPU
  !movntq [edi+16], mm2  ;    from READING the destination address
  !movntq [edi+24], mm3  ;    into the cache, only to be over-written,
  !movntq [edi+32], mm4  ;    so that also helps performance
  !movntq [edi+40], mm5
  !movntq [edi+48], mm6
  !movntq [edi+56], mm7
  !ADD  edi, 64    ; update dest pointer
  !DEC  eax     ; count down
  !JNZ  l_memcpy_bp_3  ; keep copying
  !SUB  ecx, 128  ; update the 64-byte block count
  !JMP  l_memcpy_bp_1  ; keep processing chunks
  ;The smallest copy uses the X86 "!movsd" instruction, in an optimized
  ;form which is an "unrolled loop".   Then it handles the last few bytes.
  !ALIGN 4
  !movsd
  !movsd   ; perform last 1-15 dword copies
  !movsd
  !movsd
  !movsd
  !movsd
  !movsd
  !movsd
  !movsd
  !movsd   ; perform last 1-7 dword copies
  !movsd
  !movsd
  !movsd
  !movsd
  !movsd
  !movsd
  memcpy_last_few:  ; dword aligned from before !movsd's
  !MOV  ecx, ebx ; has valid low 2 bits of the byte count
  !And  ecx, 3 ; %11 ; the last few cows must come home
  !JZ  l_memcpy_final ; no more, let's leave
  !REP  movsb  ; the last 1, 2, or 3 bytes
  memcpy_final:
  !emms    ; clean up the  state
  !sfence    ; flush the write buffer
EndProcedure 

Structure LL_Pointer
  next_.l
  prev_.l
EndStructure

Procedure GiveMeLL(*LinkedList, Element, *Buffer, StructSize)
  If *LinkedList And *Buffer
    LL.LL_Pointer
    *LinkedList - 8
    CopyMemoryAMD(*LinkedList, @LL.LL_Pointer, SizeOf(LL_Pointer))
    While LL\prev_ <> 0
      *LinkedList = LL\prev_
      CopyMemoryAMD(*LinkedList, @LL.LL_Pointer, SizeOf(LL_Pointer))
    Wend
    
    For k=1 To Element
      If LL\next_ = 0 : Break : Else
       
       *LinkedList = LL\next_
       CopyMemoryAMD(*LinkedList, @LL.LL_Pointer, SizeOf(LL_Pointer))
       
      EndIf
    Next
    
    CopyMemoryAMD(*LinkedList+SizeOf(LL_Pointer), *Buffer, StructSize)
    
    ProcedureReturn 1
  EndIf
EndProcedure

Structure TEST_
  a.l
  b.b
  c.s
EndStructure

NewList Testing.TEST_()

For k=0 To 10
  AddElement(Testing())
  Testing()\a = k
  Testing()\b = 100-k
  Testing()\c = "I am "+Str(k)
  Debug Testing()\c
  Debug "---"
Next
FirstElement(Testing())

Debug "We catch the 5th element:"
Value.TEST_
GiveMeLL(@Testing(), 5, @Value, SizeOf(TEST_))
Debug Value\a
Debug Value\b
Debug Value\c
If you understand my code you even can do a PutIntoLL()
bye,
Daniel
User avatar
Hroudtwolf
Addict
Addict
Posts: 803
Joined: Sat Feb 12, 2005 3:35 am
Location: Germany(Hessen)
Contact:

Post by Hroudtwolf »

Thank you very much for ideas, changes and praise.
benny
Enthusiast
Enthusiast
Posts: 465
Joined: Fri Apr 25, 2003 7:44 pm
Location: end of www
Contact:

Post by benny »

@DD:

I mean natively.

Anyway. Thx 4 sharing this code. Nice work you did there :!:
regards,
benny!
-
pe0ple ar3 str4nge!!!
TerryHough
Enthusiast
Enthusiast
Posts: 781
Joined: Fri Apr 25, 2003 6:51 pm
Location: NC, USA
Contact:

Post by TerryHough »

Personally, I find this one line of code handy...

Code: Select all

ExplorerListGadget(0, 10, 10, 120, 180, "C:\*.*", #PB_Explorer_NoFolders|#PB_Explorer_NoParentFolder|#PB_Explorer_AutoSort)
to give me a sorted directory listing.

Or if I want to manipulate it a bit more... working example

Code: Select all

Procedure RemoveIcons(gad) 
  ;--> To remove the icons, we make a duplicate the current ImageList for the small icons 
  hIList = SendMessage_(GadgetID(0), #LVM_GETIMAGELIST, #LVSIL_SMALL, 0) 
  hImageList = ImageList_Duplicate_(hIList) 
  ;--> When we resize the icons, they all are removed 
  ;--> I will resize the ImageList icons to a width of 1 and height of 16 
  ImageList_SetIconSize_(hImageList, 1, 16) 
  ;--> Send the new ImageList to the ExplorerListGadget 
  SendMessage_(GadgetID(0), #LVM_SETIMAGELIST, #LVSIL_SMALL, hImageList) 
  ProcedureReturn hImageList 
EndProcedure 

If OpenWindow(0,0,0,400,200,"ExplorerListGadget",#PB_Window_SystemMenu|#PB_Window_ScreenCentered)
  ExplorerListGadget(0, 10, 10, 120, 180, "C:\*.*", #PB_Explorer_NoFolders|#PB_Explorer_NoParentFolder|#PB_Explorer_AutoSort)
  GadgetToolTip(0, "Displaying a sorted directory listing.") 
  ; Change the format if desired.  List is the default.
  ; ChangeListIconGadgetDisplay(0, 2) 
  ; Remove any unwanted columns from the gadget
  RemoveGadgetColumn(0, 3) 
  RemoveGadgetColumn(0, 2) 
  RemoveGadgetColumn(0, 1) 
  ; Optional - Remove Icons 
  RemoveIcons(0) 
  Repeat : Until WaitWindowEvent()=#PB_Event_CloseWindow
EndIf
End  
See this post for more info on manipulating the ExplorerListGadget
viewtopic.php?t=15788&highlight=explorerlistgadget
thanks to Freak and Sparkie.

I think the ExplorerListGadget() should also be referenced in the
FileSysem section of the manual. :)
Last edited by TerryHough on Thu Dec 02, 2010 3:43 pm, edited 1 time in total.
JustinJack
User
User
Posts: 89
Joined: Thu Feb 04, 2010 7:34 am
Location: Decatur, TX
Contact:

Passing Pointers to Linked List

Post by JustinJack »

In this example the thread alterList()
runs and watches the list. The functionality is
that we HAVE a pointer to a list. You can't do it directly, but
if you put a list in a structure, you can effectively pass a pointer
to the structure, which, by proxy, passes a list pointer.

ps. This code only works on windows, sorry The only
thing is the #ES_MULTILINE reference on the string
gadget, other than that it's all native...

Code: Select all

Structure myListStructure
  listMutex.l
  closeSemaphore.l
  List myList.s()
EndStructure

Procedure alterList( lParam )
  *myList.myListStructure = lParam
  myMutex = *myList\listMutex
  mySemaphore = *myList\closeSemaphore
  Repeat
    LockMutex(myMutex)
    ResetList(*myList\myList())
    While NextElement(*myList\myList())
      foundTestPOS = FindString(UCase(*myList\myList()), "TEST", 1)
      If foundTestPOS > 0
        *myList\myList() = Mid(*myList\myList(), 1, foundTestPOS-1) + "****" + Mid(*myList\myList(), foundTestPOS + 4, Len(*myList\myList()))
      EndIf
    Wend
    UnlockMutex(myMutex)
    Delay(100)
  Until TrySemaphore(mySemaphore)
EndProcedure


Procedure main()
  ; Set up list
  myList.myListStructure
  myList\listMutex = CreateMutex()
  myList\closeSemaphore = CreateSemaphore()
  localSemaphore = myList\closeSemaphore
  localMutex = myList\listMutex
  
  threadWithListPointer = CreateThread(@alterList(), @myList)
  
  hWnd = OpenWindow(1, 0, 0, 530, 340, "Linked List Pointer Test", #PB_Window_ScreenCentered|#PB_Window_SystemMenu)
  AddKeyboardShortcut(1, 27, 1)
  hStringGad        =   StringGadget(1,  10, 10, 250, 300, "", #ES_MULTILINE)
  hLinkedListViewer = ListIconGadget(2, 270, 10, 250, 300, "List Element", 75, #PB_ListIcon_GridLines)
  AddGadgetColumn(2, 1, "List Item", 170)
  GadgetToolTip(1, "Try typing in the word " + Chr(34) + "Test" + Chr(34) + " and watch the List Side")
  AddWindowTimer(1, 142, 250)
  Repeat 
    myEvent = WaitWindowEvent()
    Select myEvent
      Case #PB_Event_Timer
        Select EventTimer()
          Case 142
            LockMutex(localMutex)
            ClearGadgetItems(2)
            ResetList(myList\myList())
            ctr = 0
            While NextElement(myList\myList())
              AddGadgetItem(2, -1, Str(ctr) + Chr(10) + myList\myList())
              ctr + 1
            Wend
            UnlockMutex(localMutex)
        EndSelect
        
      Case #PB_Event_Gadget
        Select EventGadget()
          Case 1
            Select EventType()
              Case #PB_EventType_Change
                LockMutex(localMutex)
                ClearList(myList\myList())
                myText.s = GetGadgetText(1)
                For k = 1 To (CountString(myText, Chr(10)) + 1)
                  AddElement(myList\myList())
                  myList\myList() = StringField(myText, k, Chr(10))
                Next
                UnlockMutex(localMutex)
            EndSelect
        EndSelect
        
      Case #PB_Event_Menu
        Select EventMenu()
          Case 1
            myEvent = #PB_Event_CloseWindow
        EndSelect
    EndSelect
  Until (myEvent = #PB_Event_CloseWindow)
  SignalSemaphore(localSemaphore)
  WaitThread(threadWithListPointer)
  
EndProcedure

main()
Post Reply