QB64.org Forum

Active Forums => QB64 Discussion => Topic started by: sparjolly on October 26, 2020, 10:06:32 am

Title: DIR$ function is missing
Post by: sparjolly on October 26, 2020, 10:06:32 am
My old BASIC code that was working with 32-bit QBASIC compiler now shows error in QB64 on the following line with DIR$ function. Is there any way to walk around this issue?

F$=DIR$("*.LL1")
Title: Re: DIR$ function is missing
Post by: SpriggsySpriggs on October 26, 2020, 10:18:42 am
Is the code supposed to show an output of files that match that wildcard? If so, I have some code that could help you.
Title: Re: DIR$ function is missing
Post by: SpriggsySpriggs on October 26, 2020, 10:26:15 am
Here is some code that can help you. You need to download the attached "pipecom.h" file and put the DECLARE LIBRARY block in your program. The header contains code that pipes console output into the variable for printing, manipulating, etc.

Code: QB64: [Select]
  1. DECLARE LIBRARY "pipecom"
  2.     FUNCTION pipecom$ (cmd AS STRING)
  3.  
  4.  
  5. F = pipecom("dir *.BAS")
  6. 'F = pipecom("dir /b *.BAS") 'if you need just the filenames and not the rest of the output
  7.  

And a sample printout:

  [ This attachment cannot be displayed inline in 'Print Page' view ]    [ This attachment cannot be displayed inline in 'Print Page' view ]  
Title: Re: DIR$ function is missing
Post by: FellippeHeitor on October 26, 2020, 10:28:40 am
QB64 is aimed at close to 100% compatibility with QBasic/QuickBASIC 4.5. DIR$() used to be available in PDS7. Here's an adaptation, from the wiki (http://www.qb64.org/wiki/FILES) (although I'd go with Spriggsy's approach above):

Quote
The DIR$ function adapted from PDS (7.1) returns a filename or a list when more than one exist. The file spec can use a path and/or wildcards.
Code: QB64: [Select]
  1. FOR i = 1 TO 2
  2.   LINE INPUT "Enter a file spec: ", spec$
  3.   file$ = DIR$(spec$) 'use a file spec ONCE to find the last file name listed
  4.   PRINT DIRCount%, file$, 'function can return the file count using SHARED variable
  5.   IF DIRCount% > 1 THEN
  6.     DO
  7.       K$ = INPUT$(1)
  8.       file$ = DIR$("") 'use an empty string parameter to return a list of files!
  9.       PRINT file$,
  10.     LOOP UNTIL LEN(file$) = 0 'file list ends with an empty string
  11.   END IF
  12.  
  13.  
  14. FUNCTION DIR$ (spec$)
  15. CONST TmpFile$ = "DIR$INF0.INF", ListMAX% = 500 'change maximum to suit your needs
  16. SHARED DIRCount% 'returns file count if desired
  17. STATIC Ready%, Index%, DirList$()
  18. IF NOT Ready% THEN REDIM DirList$(ListMAX%): Ready% = -1 'DIM array first use
  19. IF spec$ > "" THEN 'get file names when a spec is given
  20.   SHELL _HIDE "DIR " + spec$ + " /b > " + TmpFile$
  21.   Index% = 0: DirList$(Index%) = "": ff% = FREEFILE
  22.   OPEN TmpFile$ FOR APPEND AS #ff%
  23.   size& = LOF(ff%)
  24.   CLOSE #ff%
  25.   IF size& = 0 THEN KILL TmpFile$: EXIT FUNCTION
  26.   OPEN TmpFile$ FOR INPUT AS #ff%
  27.   DO WHILE NOT EOF(ff%) AND Index% < ListMAX%
  28.     Index% = Index% + 1
  29.     LINE INPUT #ff%, DirList$(Index%)
  30.   LOOP
  31.   DIRCount% = Index% 'SHARED variable can return the file count
  32.   CLOSE #ff%
  33.   KILL TmpFile$
  34. ELSE IF Index% > 0 THEN Index% = Index% - 1 'no spec sends next file name
  35. DIR$ = DirList$(Index%)
Title: Re: DIR$ function is missing
Post by: bplus on October 26, 2020, 10:53:26 am
There is a non-Windows solution too but a little more work.
Title: Re: DIR$ function is missing
Post by: SpriggsySpriggs on October 26, 2020, 10:54:30 am
There is a non-Windows solution too but a little more work.
@bplus Guess what? Mine is cross-platform ;) Works on Linux, Mac, and Windows. All the same code.
Title: Re: DIR$ function is missing
Post by: bplus on October 26, 2020, 10:56:09 am
@bplus Guess what? Mine is cross-platform ;) Works on Linux, Mac, and Windows. All the same code.

Oh cool!

@SpriggsySpriggs  had you posted this? I must have missed.
Title: Re: DIR$ function is missing
Post by: luke on October 26, 2020, 11:14:10 am
Obligatory friendly reminder that Linux is case sensitive for file names, so "dir *.BAS" won't match "foo.bas".

Also on Linux and MacOS the default output is plain filenames, "ls -l" is needed for a full listing with metadata.
Title: Re: DIR$ function is missing
Post by: SpriggsySpriggs on October 26, 2020, 11:15:49 am
Obligatory friendly reminder that Linux is case sensitive for file names, so "dir *.BAS" won't match "foo.bas".

Also on Linux and MacOS the default output is plain filenames, "ls -l" is needed for a full listing with metadata.
@luke Yes, of course. I didn't mean that "dir /b *.BAS" was cross-platform. Only that the pipecom library was :)
Title: Re: DIR$ function is missing
Post by: sparjolly on October 26, 2020, 12:17:57 pm
Thanks for your suggestion. The issue is resolved.
Title: Re: DIR$ function is missing
Post by: SpriggsySpriggs on October 26, 2020, 12:19:49 pm
Thanks for your suggestion. The issue is resolved.
@sparjolly Glad to hear!
Title: Re: DIR$ function is missing
Post by: SpriggsySpriggs on October 26, 2020, 12:22:04 pm
Oh cool!

@SpriggsySpriggs  had you posted this? I must have missed.
@bplus I hadn't posted it. I have been keeping it on the Discord server only while I've been testing it across platforms. Although, I reckon I could make a post for it.
Title: Re: DIR$ function is missing
Post by: sparjolly on October 26, 2020, 07:57:01 pm
I am back again. While the "pipecom.h" library provides a solution, it captures all the file names in the folder meeting the specified criterion. Having a folder with hundreds of files will require creating a huge string array to store all the file names in the program.

The original code using DIR$ function of BASIC fetches only one name at a time, which is a more elegant solution.

I wonder if the "while (!feof(stream))" statement be pulled out of the library so that it provides only one file name at a time and returns a nul when all the files are covered.

Thanks


F$=DIR$("*.LL1")

DO WHILE F$ <> ""

    CNT$ = LEFT$(F$, 4)

    OPEN CNT$ + ".LL1" FOR INPUT AS #1
    OPEN CNT$ + ".LL" FOR OUTPUT AS #2

    F$ = DIR$

LOOP
Title: Re: DIR$ function is missing
Post by: SpriggsySpriggs on October 26, 2020, 08:28:30 pm
@sparjolly Here you go. This uses a method of splitting a string up into an array based on a delimiter. You can see that I have split the string into a new string array called filelist. You can then take it and do your operations on the files within. I would have to write up a new function for the library that uses the original stream and I don't think it would work the way you are wanting it to. For now, try the below code.

Code: QB64: [Select]
  1. DECLARE LIBRARY "pipecom"
  2.     FUNCTION pipecom$ (cmd AS STRING)
  3.  
  4.  
  5. F = pipecom("dir /b *.BAS")
  6.  
  7. REDIM filelist(0) AS STRING
  8.  
  9. String.Split F, CHR$(10), filelist(), 0
  10.  
  11. FOR x = 0 TO UBOUND(filelist) - 1
  12.     PRINT filelist(x)
  13.  
  14.  
  15. SUB String.Split (Expression AS STRING, delimiter AS STRING, StorageArray() AS STRING, preserve AS INTEGER)
  16.     DIM copy AS STRING, p AS LONG, curpos AS LONG, arrpos AS LONG, dpos AS LONG
  17.     copy = Expression
  18.     IF delimiter = " " THEN
  19.         copy = RTRIM$(LTRIM$(copy))
  20.         p = INSTR(copy, "  ")
  21.         WHILE p > 0
  22.             copy = MID$(copy, 1, p - 1) + MID$(copy, p + 1)
  23.             p = INSTR(copy, "  ")
  24.         WEND
  25.     END IF
  26.     curpos = 1
  27.     IF preserve <> 0 THEN
  28.         arrpos = UBOUND(StorageArray)
  29.         dpos = INSTR(curpos, copy, delimiter)
  30.         DO UNTIL dpos = 0
  31.             StorageArray(UBOUND(StorageArray)) = MID$(copy, curpos, dpos - curpos)
  32.             REDIM _PRESERVE StorageArray(UBOUND(StorageArray) + 1) AS STRING
  33.             curpos = dpos + LEN(delimiter)
  34.             dpos = INSTR(curpos, copy, delimiter)
  35.         LOOP
  36.         StorageArray(UBOUND(StorageArray)) = MID$(copy, curpos)
  37.         REDIM _PRESERVE StorageArray(UBOUND(StorageArray) + 1) AS STRING
  38.     ELSEIF preserve = 0 THEN
  39.         arrpos = 0
  40.         dpos = INSTR(curpos, copy, delimiter)
  41.         DO UNTIL dpos = 0
  42.             StorageArray(arrpos) = MID$(copy, curpos, dpos - curpos)
  43.             arrpos = arrpos + 1
  44.             IF arrpos > UBOUND(StorageArray) THEN REDIM _PRESERVE StorageArray(UBOUND(StorageArray) + 1) AS STRING
  45.             curpos = dpos + LEN(delimiter)
  46.             dpos = INSTR(curpos, copy, delimiter)
  47.         LOOP
  48.         StorageArray(arrpos) = MID$(copy, curpos)
  49.         REDIM _PRESERVE StorageArray(arrpos) AS STRING
  50.     END IF
Title: Re: DIR$ function is missing
Post by: doppler on October 26, 2020, 08:57:44 pm
@SpriggsySpriggs Looking at best answer it's no different than

DIM F AS STRING
CHDIR "c:\qb64\my programs"

shell "dir /b *.bas | clip"
f=_clipboard$

PRINT F

But which way would be faster (by code execution time) ?  Which one could hold more data ? (me thinks windows clipboard)
Title: Re: DIR$ function is missing
Post by: SpriggsySpriggs on October 26, 2020, 09:07:01 pm
@SpriggsySpriggs Looking at best answer it's no different than

DIM F AS STRING
CHDIR "c:\qb64\my programs"

shell "dir /b *.bas | clip"
f=_clipboard$

PRINT F

But which way would be faster (by code execution time) ?  Which one could hold more data ? (me thinks windows clipboard)
@doppler
You say it is not different but it actually is quite different. I'm reading in a buffer of data and returning the buffer. Your way shows saving data to the clipboard and reading it. That would be annoying for people who use the clipboard and have things saved to it. If they aren't using the new Clipboard History feature of Windows 10 then you are just having them overwrite the last thing they copied. And as for holding data, you are holding quite literally the same amount of data whether you use my way or your way. Also, my way is faster as you can see from the screenshot. Your way slows down because you are "piping" to clip making the command prompt have to do more things.

My test code:
Code: QB64: [Select]
  1. DECLARE LIBRARY "pipecom"
  2.     FUNCTION pipecom$ (cmd AS STRING)
  3.  
  4. x# = TIMER(0.01)
  5. F = pipecom("dir /b *.BAS")
  6.  
  7. y# = TIMER(0.01)
  8. PRINT "Pipecom:", y# - x#
  9.  
  10. x# = TIMER(0.01)
  11. SHELL "dir /b *.BAS | clip"
  12.  
  13. y# = TIMER(0.01)
  14. PRINT "Doppler:", y# - x#

  [ This attachment cannot be displayed inline in 'Print Page' view ]  
Title: Re: DIR$ function is missing
Post by: bplus on October 26, 2020, 10:22:29 pm
@SpriggsySpriggs Looking at best answer it's no different than

DIM F AS STRING
CHDIR "c:\qb64\my programs"

shell "dir /b *.bas | clip"
f=_clipboard$

PRINT F

But which way would be faster (by code execution time) ?  Which one could hold more data ? (me thinks windows clipboard)

But can you get Clipboard contents after a shell? But I hadn't tried through Windows clip that would be cool. I've tried through QB64 clipboard and no luck, maybe my slants were backwards ;-))

Nope:
Code: QB64: [Select]
  1. SHELL "dir /b *.BAS | clip"
  2.  
  3.  

Crap I forgot this runs files in QB64 directory until saved, it did work! I didn't recognize the contents ;-))
Title: Re: DIR$ function is missing
Post by: doppler on October 27, 2020, 06:58:40 pm
@SpriggsySpriggs
I suspected the shell way would be slower.  If you don't mind destroying the clipboard the data objective is the same. I may just play again with some working code to use your pipe.h  I need to use the cmd /c with windows string search findstr.  I decided against F.A.R.T. ing it.  I will us FART on other stuff.  I have a large filename dataset to search against an even larger text file of filenames.  And I have to check for variations of names.  That's an awful lot of (for i,j,k ... next k,j,i ) loops.  I will take the speed up pipe could give.

My toybox is becoming very full.  I need to get a bigger one.

@bplus Just remember clipboard will be zero on failed content and be delimited by cr/lf's  Using the clipboard to push around same chunks of data is the way Microsoft suggests to be used in .net.  I guess the big stuff should go thru DCOM interface.
Title: Re: DIR$ function is missing
Post by: bplus on October 27, 2020, 07:05:28 pm
@doppler  man! this is so simple for 2-3 years I've been looking for something half this good then Spriggsy shows a real break through and then you show a better! You guys get my nomination for Tip of the Month!
Title: Re: DIR$ function is missing
Post by: SpriggsySpriggs on October 27, 2020, 07:57:22 pm
@doppler  man! this is so simple for 2-3 years I've been looking for something half this good then Spriggsy shows a real break through and then you show a better! You guys get my nomination for Tip of the Month!
@bplus My main complaint against using "clip" is that in doing so you are possibly showing the user how your code works which is not always something you want to do. You really want your functions to be hidden because if they just start seeing console output in their clipboard then the magic is lost. Also, like I was telling doppler, you don't want to overwrite the clipboard as many people don't have Windows 10 and those who do haven't enabled Clipboard History so you would be overwriting something they might want on the clipboard. If you are going to overwrite their clipboard it might be wise to tell them first or read the clipboard into a string, do your thing, then copy it back to the clipboard.
Title: Re: DIR$ function is missing
Post by: bplus on October 27, 2020, 08:04:40 pm
@SpriggsySpriggs Well I think we can save the contents get the info and then replace the contents if that is your concern.

I am not sure how the user is going to see the SHELL, I think there is HIDE option but so what if user sees?

Say does Linux have | clip option?





Title: Re: DIR$ function is missing
Post by: SpriggsySpriggs on October 27, 2020, 08:25:46 pm
@bplus I wasn't saying seeing the SHELL; I was talking about seeing the console output in their clipboard when they go to paste something they might have copied prior the program being ran. Linux does have a similar command but it doesn't come pre-installed. Here is some info concerning that. https://opensource.com/article/19/7/xclip (https://opensource.com/article/19/7/xclip)

By the way, remember my recursive directory search using PowerShell? Yeah, it doesn't work piping it to clip. It does, however, work fine in pipecom. In 33 seconds I obtained the ENTIRE recursive listing of EVERY SINGLE FILE in EVERY SINGLE FOLDER in Program Files (x86) which resulted in a string of 24,655,989 bytes. According to Google, the maximum you can store in a Windows 10 clipboard for one item is 4 megabytes, or 4,000,000 bytes which is why piping to clip does not work with my command.

Code: QB64: [Select]
  1.  
  2. DECLARE LIBRARY "pipecom"
  3.     FUNCTION pipecom$ (cmd AS STRING)
  4.  
  5.  
  6. x# = TIMER(0.01)
  7. Path$ = "C:\Program Files (x86)" '\Steam\steamapps\common"
  8. comm = pipecom("PowerShell Get-ChildItem -Path \" + CHR$(34) + Path$ + "\" + CHR$(34) + " -Recurse -Force -ErrorAction SilentlyContinue -Attributes Hidden, !Hidden ^| Select-Object -ExpandProperty FullName ^| Sort-Object ^| Format-Table -AutoSize")
  9.  
  10. y# = TIMER(0.01)
  11. PRINT comm, LEN(comm)
  12. PRINT y# - x#; " seconds"
Title: Re: DIR$ function is missing
Post by: bplus on October 27, 2020, 09:01:57 pm
OK all better!

Code: QB64: [Select]
  1. save$ = _CLIPBOARD$
  2. SHELL "dir /b *.BAS | clip" ' < Please note clipboard holds only a max 4,000,000+ bytes so stay out of Windows folders ;-))
  3. PRINT _CLIPBOARD$ ' or save the string to split later and process
  4. _CLIPBOARD$ = save$
  5.  
  6. '!!!!!!!!!!!!!!!!! comment out the code below that was test paste from clipboard before testing above code
  7.  
  8. 'copied this to clipboard before test run of code above then pasted here after run ========================================
  9. save$ = _CLIPBOARD$
  10. SHELL "dir /b *.BAS | clip" ' < Please note clipboard holds only a max 4,000,000+ bytes so stay out of Windows folders ;-))
  11. PRINT _CLIPBOARD$ ' or save the string to split later and process
  12. _CLIPBOARD$ = save$
  13. '================================================================ looks good!
  14.  
  15.  

;-))