' lbbTree v1.0 on 2017-07-17 by CirothUngol using LB Booster v3.08
' Compile as a console app, otherwise output is sent to MainWin.
' Intended as an improved replacement for Windows' TREE.EXE.
'
' lbbTree [drive:][path] [/A] [/C] [/F] [/M] [/O] [/S filter] [/T] [/?]
'
' /A Use ASCII instead of extended characters.
' /C Use alternative character set.
' /F Display the names of the files in each folder.
' /L Use less spacing for the margins.
' /M Display more folder/file information.
' /O Display only folders that contain files.
' /S Search for file names matching filter. */? are allowed.
' /T Truncate display to 79 characters per line.
' /? Display this help screen.
'
' Other differences:
' Always displays the PATH (TREE displays 'drive:.' if no path provided)
' Displays PATH as found on disk (TREE displays all uppercase)
' Displays search filter if provided (TREE doesn't filter file names)
' Displays version number on help screen (TREE doesn't provide it)
' Accepts input path with a trailing backslash (TREE rejects as invalid)
' Accepts switches enclosed in double-quotes (TREE assumes a PATH)
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
ver$ = "1.0"
cr$ = CHR$(13)
'''''''''''''''''''''''
' parse the commandline
x = cmdln(CommandLine$,1)
WHILE x > y
y += 1
SELECT CASE LOWER$(cmdln$(y))
CASE "/a" ' use Ascii text
IF mode AND 1 THEN CALL errOut "Parameter format not correct - "; cmdln$(y)
mode += 1
CASE "/c" ' use alternate Characters
IF mode AND 2 THEN CALL errOut "Parameter format not correct - "; cmdln$(y)
mode += 2
CASE "/o" ' Only folders containing files
IF mode AND 4 THEN CALL errOut "Parameter format not correct - "; cmdln$(y)
mode += 4
CASE "/f" ' display File names
IF mode AND 8 THEN CALL errOut "Parameter format not correct - "; cmdln$(y)
mode += 8
CASE "/m" ' display More info
IF mode AND 16 THEN CALL errOut "Parameter format not correct - "; cmdln$(y)
mode += 16
CASE "/t" ' truncate display to 79 characters
IF mode AND 32 THEN CALL errOut "Parameter format not correct - "; cmdln$(y)
mode += 32
CASE "/l" ' use less spacing in margins
IF mode AND 64 THEN CALL errOut "Parameter format not correct - "; cmdln$(y)
mode += 64
CASE "/s" ' Search for file names
IF mode AND 128 THEN CALL errOut "Parameter format not correct - "; cmdln$(y); " "; cmdln$(y+1)
mode += 128
y += 1
IF x >= y THEN filter$ = cmdln$(y)
CASE "/?" ' display help screen
CALL lbbTreeHelp ver$
CASE ELSE ' drive:\path
IF LEFT$(cmdln$(y),1) = "/" THEN CALL errOut "Invalid switch - "; cmdln$(y)
IF path$ <> "" THEN
x = cmdln(CommandLine$,0)
CALL errOut "Too many parameters - "; cmdln$(y)
END IF
path$ = cmdln$(y)
END SELECT
WEND
X = dirExists(path$)
IF X = -1 THEN CALL errOut "Invalid drive specification"
'''''''''''''''''''''''''''''''''''
' get volume name and serial number
p$ = LEFT$(path$,2); "\"
vName$ = SPACE$(_MAX_PATH); CHR$(0)
vNameSize = _MAX_PATH+1
STRUCT vSerial, sn as ulong
CALLDLL #kernel32, "GetVolumeInformationA",_
p$ as ptr,_
vName$ as ptr,_
vNameSize as ulong,_
vSerial as struct,_
0 as long, 0 as long, 0 as long, 0 as long,_
result as boolean
IF result = 0 THEN CALL errOut "Invalid volume specification"
vSerial$ = RIGHT$("0000000"; dechex$(vSerial.sn.struct),8)
PRINT "Folder PATH listing for volume "; LEFT$(vName$,INSTR(vName$,CHR$(0))-1)
PRINT "Volume serial number is "; LEFT$(vSerial$,4); "-"; RIGHT$(vSerial$,4)
IF X = 0 THEN CALL errOut path$;cr$;"Invalid path - ";MID$(path$,3);cr$;"No subfolders exist"
CALL lbbTree path$, filter$, lbbTree$(), mode
END
''''''''''''''''''''''''''''''''
' display error message and exit
SUB errOut display$
PRINT display$
END
END SUB
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'NAME=cmdln
'AUTHOR=CirothUngol
'ITEM=FUNCTION cmdln(cl$, mode)
'DESCRIPTION=Breaks cl$ into parameters, stores them in cmdln$(), and returns # of parameters.
FUNCTION cmdln(cl$, mode)
' cmdln$(0) = # of items in array.
' mode +0 - Retain double-quotes on parameters
' mode +1 - Remove double-quotes from parameters
d$ = CHR$(0)
q$ = CHR$(34)
cl$ = TRIM$(cl$)
mode = mode AND 1
WHILE cl$ <> ""
IF LEFT$(cl$,1) = q$ THEN
p = INSTR(cl$,q$,2)
IF p = 0 THEN p$ = MID$(cl$,1+mode)
IF p > 0 THEN p$ = MID$(cl$,1+mode,p-mode*2)
cl$ = MID$(cl$,LEN(p$)+mode*2+1)
ELSE
p$ = WORD$(cl$,1)
cl$ = MID$(cl$,LEN(p$)+1)
END IF
param$ = param$; p$; d$
cmdln += 1
cl$ = TRIM$(cl$)
WEND
REDIM cmdln$(cmdln)
cmdln$(0) = STR$(cmdln)
FOR p = 1 TO cmdln
cmdln$(p) = WORD$(param$,p,d$)
NEXT p
END FUNCTION
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'NAME=dirExists
'AUTHOR=CirothUngol
'ITEM=FUNCTION dirExists(BYREF path$)
'DESCRIPTION=Resolves and saves path$. Return 1=path exists, -1=invalid drive, 0=invalid folder.
FUNCTION dirExists(BYREF path$)
'Arguments:
'path$ - Name of FolderPath to check for existence. If Full Path
' is not provided, then StartupDir$ is used.
IF path$ = "" THEN
path$ = StartupDir$
dirExists = 1
EXIT FUNCTION
END IF
' resolve relative paths and save to path$
IF INSTR(path$,"\") = 1 THEN path$ = LEFT$(StartupDir$,2); path$
IF INSTR(path$,":") <> 2 THEN path$ = StartupDir$; "\"; path$
FILES path$, "\", x$()
path$ = x$(0,2); x$(0,3)
path$ = LEFT$(path$,LEN(path$)-1)
' check for invalid drive
d$ = LOWER$(LEFT$(path$,2))
DO
X += 1
t$ = WORD$(Drives$,X)
LOOP UNTIL (t$ = "") OR (t$ = d$)
IF t$ = "" THEN
dirExists = -1
EXIT FUNCTION
END IF
' check for invalid folder & correct path capitalization
d$ = UPPER$(LEFT$(path$,2))
p$ = LOWER$(path$)
t$ = WORD$(p$,2,"\")
X = 2
WHILE t$ <> ""
FILES d$, "\", x$()
FOR i = 1 TO VAL(x$(0,1))
IF LOWER$(x$(i,1)) = t$ THEN d$ = d$; "\"; x$(i,1)
NEXT i
X += 1
t$ = WORD$(p$,X,"\")
WEND
IF LOWER$(d$) <> p$ THEN EXIT FUNCTION
' drive:\dir exists, save path and exit
dirExists = 1
path$ = d$
END FUNCTION
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'NAME=fileCount
'AUTHOR=CirothUngol
'ITEM=FUNCTION fileCount(path$, filter$, mode)
'DESCRIPTION=Recursive count of path$\filter$. Returns number of occurances.
FUNCTION fileCount(path$, filter$, mode)
' If path$ is empty, StartupDir$ is assumed. If filter$ is empty, "*" is assumed.
' Return mode 0=num of files, 1=files + folders. Set filter$="\" for folders only.
IF path$ = "" THEN path$ = StartupDir$
IF filter$ = "" THEN filter$ = "*"
FILES path$, filter$, x$()
numFiles = VAL(x$(0,0))
numFolders = VAL(x$(0,1))
fileCount = numFiles
IF mode AND 1 THEN fileCount += numFolders
' recurse through all found folders
FOR count = 1 TO numFolders
subPath$ = subPath$; x$(0,2); x$(0,3); x$(numFiles+count,1); ">"
NEXT count
FOR count = 1 TO numFolders
fileCount += fileCount(WORD$(subPath$,count,">"), filter$, mode)
NEXT count
END FUNCTION
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'NAME=rFiles
'AUTHOR=CirothUngol
'ITEM=FUNCTION rFiles(path$, filter$, BYREF a$())
'DESCRIPTION=Recursive search for path$\filter$. Loads a$() with file/folder info. Returns number of files found.
FUNCTION rFiles(path$, filter$, BYREF a$())
' This function is modeled directly on the LB Booster FILES function, and the array a$() that
' it builds is intended to closely mirror that which is created by FILES. The arguments are:
' path$ - The root folder to recursively search for files. If empty, StartupDir$ is assumed.
' filter$ - The file name to search for. Wildcards are allowed. If empty, "*" is assumed.
' a$() - 2D array passed BYREF is REDIMed and loaded with file/folder info.
'
' Much like the FILES statement, rFiles() will fill the array a$() in this fashion:
' a$(0, 0) - The number of files found
' a$(0, 1) - The number of folders found, including root folder
' a$(0, 2) - The root drive spec
' a$(0, 3) - The root directory path
' a$(0, 4) - Total size of all files found
' a$(0, 5) - Full root directory path
'
' Then starting at a$(1, y), you will have the following file information:
' a$(x, 0) - The file name
' a$(x, 1) - The file size
' a$(X, 2) - The file date/time "MM/DD/YY 12:mm:ss AM|PM" (local time)
' a$(X, 3) - The file attributes
' a$(X, 4) - The file date/time "YYYY-MM-DD 24:mm:ss" (GMT)
' a$(x, 5) - Additional Path\To\
'
' totalFiles = VAL(a$(0, 0))
' folder information starts with the root folder at a$(totalFiles + 1, y):
' a$(totalFiles + x, 0) - The folder name
' a$(totalFiles + x, 1) - Total size of files found in folder
' a$(totalFiles + x, 2) - Index to 1st file in folder, or blank if none
' a$(totalFiles + x, 3) - Number of files found in folder
' a$(totalFiles + x, 4) - Number of subfolders found in folder
' a$(totalFiles + x, 5) - Additional Path\To\
'
' Using this info, you can reconstruct the full path to file/folder X thusly:
' fullPath$ = a$(0, 5); a$(X, 5); a$(X, 0)
''''''''''''''''''''''''''''''''''''''''''
IF path$ = "" THEN path$ = StartupDir$
IF filter$ = "" THEN filter$ = "*"
rFiles = fileCount(path$,filter$,0)
totFolders = fileCount(path$,"\",1) + 1 ' +1 for the root folder
fi = 0 ' file index
di = rFiles ' folder index
REDIM a$(rFiles + totFolders,5)
FILES path$, "\", x$()
a$(0,0) = STR$(rFiles)
a$(0,1) = STR$(totFolders)
a$(0,2) = UPPER$(x$(0,2))
a$(0,3) = x$(0,3)
a$(0,4) = STR$(rFiles1(path$, "", filter$, LEN(a$(0,3))+1, fi, di))
a$(0,5) = a$(0,2); a$(0,3)
END FUNCTION
FUNCTION rFiles1(path$, folderName$, filter$, rootLen, BYREF fi, BYREF di)
' recursively fills a$(), return value is total size of all files in folder
FILES path$; folderName$, filter$, x$()
numFiles = VAL(x$(0,0))
numFolders = VAL(x$(0,1))
IF numFiles > 0 THEN tmpIndex$ = STR$(fi + 1)
' copy file info
FOR count = 1 TO numFiles
fi += 1
a$(fi,0) = x$(count,0) ' file name
a$(fi,1) = x$(count,1) ' file size
a$(fi,2) = x$(count,2) ' date/time
a$(fi,3) = x$(count,3) ' attributes
a$(fi,4) = x$(count,4) ' date/time GMT
a$(fi,5) = MID$(x$(0,3),rootLen) ' path\to\
rFiles1 += VAL(x$(count,1)) ' total file size
NEXT count
' copy folder info
di += 1
a$(di,0) = folderName$ ' folder name
a$(di,1) = STR$(rFiles1) ' total file size
a$(di,2) = tmpIndex$ ' index to 1st file
a$(di,3) = x$(0,0) ' num of files
a$(di,4) = x$(0,1) ' num of folders
a$(di,5) = MID$(path$, rootLen+3) ' path\to
' save subfolder info and recurse
subPath$ = x$(0,2); x$(0,3)
FOR count = 1 TO numFolders
subFolders$ = subFolders$; x$(numFiles+count,1); ">"
NEXT count
FOR count = 1 TO numFolders
rFiles1 += rFiles1(subPath$,WORD$(subFolders$,count,">"),filter$,rootLen,fi,di)
NEXT count
END FUNCTION
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
'NAME=lbbTree
'AUTHOR=CirothUngol
'ITEM=SUB lbbTree path$, filter$, BYREF a$(), mode
'DESCRIPTION=displays files/folders in a DOS TREE style format
SUB lbbTree path$, filter$, BYREF a$(), mode
' This function outputs a recursive folder tree similar to the DOS TREE command.
' path$ - The root folder to recursively search for files. If empty, StartupDir$ is assumed.
' filter$ - The file name to search for. Wildcards are allowed. If empty, "*" is assumed.
' a$() - 2D array passed BYREF is REDIMed and loaded with file/folder info.
' mode - An integer value to control the output:
' +0 is extended text mode.
' +1 is ascii text mode.
' +2 use alternate character set.
' +4 return only folders that contain files.
' +8 display file names in each folder.
' +16 display more file/folder info.
' +32 truncate display to 79 characters
' +64 half-sized margins
SELECT CASE mode MOD 4 ' /a + /c character sets
CASE 0
margin$ = " ;ÀÄÄÄ;³ ;ÃÄÄÄ"
IF mode AND 64 THEN margin$ = " ;ÀÄ;³ ;ÃÄ"
CASE 1
margin$ = " ;\---;| ;+---"
IF mode AND 64 THEN margin$ = " ;\-;| ;+-"
CASE 2
margin$ = " ;ÈÍÍÍ;º ;ÌÍÍÍ"
IF mode AND 64 THEN margin$ = " ;ÈÍ;º ;ÌÍ"
CASE 3
margin$ = " ;\___;| ;|___"
IF mode AND 64 THEN margin$ = " ;\_;| ;|_"
END SELECT
IF filter$ = "" THEN filter$ = "*"
di = rFiles(path$, filter$, a$())
tmp$ = a$(0,2); a$(0,3)
IF mode AND 8 THEN tmp$ += filter$ ' /f show filter$ if provided
IF mode AND 32 THEN tmp$ = LEFT$(tmp$,79)
PRINT tmp$;
IF mode AND 4 THEN ' /o only folders containing files
pi = di
IF lbbTree2(pi) = 0 THEN ' only the root folder found
PRINT
PRINT "No files found"
PRINT "No subfolders exist"
EXIT SUB
END IF
' remove marked folders from a$() and cascade down
ni = di ' new index
li = di + VAL(a$(0,1)) ' last index
FOR oi = di+1 TO li ' old index
IF a$(oi,0) <> "<" THEN
ni += 1
FOR i = 0 TO 5
a$(ni,i) = a$(oi,i)
NEXT i
END IF
NEXT oi
a$(0,1) = STR$(ni-di) ' new numFolders
END IF
CALL lbbTree1 fi, di, m$, fm$, dm$, mode, margin$
IF a$(0,1) = "1" THEN ' only the root folder
PRINT "No subfolders exist"
EXIT SUB
END IF
IF NOT(mode AND 16) THEN EXIT SUB ' not /m, so exit
tmp$ = TRIM$(USING("###,###,###,###,###",a$(0,4))); " bytes in "; TRIM$(USING("###,###,###",a$(0,0))); " files and "; TRIM$(USING("###,###,###",a$(0,1))); " folders"
PRINT LEFT$("--------------------------------------------------------------------------------", LEN(tmp$))
PRINT tmp$
END SUB
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
SUB lbbTree1 BYREF fi, BYREF di, m$, fm$, dm$, mode, margin$ ' recursively display a$()
di += 1 ' directory index
numFolders = VAL(a$(di, 4))
tmp$ = m$; dm$; a$(di,0)
IF mode AND 16 THEN tmp$ += " / "; a$(di,1); " / "; a$(di,3); " files / "; a$(di,4); " folders"
IF mode AND 32 THEN tmp$ = LEFT$(tmp$,79)
PRINT tmp$
IF mode AND 8 THEN ' /f display files
IF numFolders = 0 THEN fm2$ = WORD$(margin$,1,";")
IF numFolders > 0 THEN fm2$ = WORD$(margin$,3,";")
numFiles = VAL(a$(di,3))
FOR f = 1 TO numFiles
fi += 1 ' file index
tmp$ = m$; fm$; fm2$; a$(fi,0)
IF mode AND 16 THEN tmp$ += " / "; a$(fi,1); " / "; a$(fi,4)
IF mode AND 32 THEN tmp$ = LEFT$(tmp$,79)
PRINT tmp$
NEXT f
tmp$ = MID$(TRIM$("A";m$;fm$;fm2$),2)
IF mode AND 32 THEN tmp$ = LEFT$(tmp$,79)
IF numFiles > 0 THEN PRINT tmp$
END IF
IF fm$ <> "" THEN m$ = m$; fm$ ' if not the 1st recursion
FOR d = 1 TO numFolders
IF d = numFolders THEN
CALL lbbTree1 fi, di, m$, WORD$(margin$,1,";"), WORD$(margin$,2,";"), mode, margin$
ELSE
CALL lbbTree1 fi, di, m$, WORD$(margin$,3,";"), WORD$(margin$,4,";"), mode, margin$
END IF
NEXT d
END SUB
'''''''''''''''''''''
FUNCTION lbbTree2(BYREF pi) ' removes empty folders from the tree
pi += 1 ' progressive index
ri = pi ' root index
numFolders = VAL(a$(ri,4))
FOR d = 1 TO numFolders
IF lbbTree2(pi) = 0 THEN a$(ri,4) = STR$(VAL(a$(ri,4))-1) ' -1 folder
NEXT d
lbbTree2 = VAL(a$(ri,3)) + VAL(a$(ri,4)) ' numFiles + numFolders
IF lbbTree2 = 0 THEN a$(ri,0) = "<" ' mark folder for deletion
END FUNCTION
'''''''''''''''
SUB lbbTreeHelp ver$
ver$ = RIGHT$(" v";ver$, 18)
PRINT "Graphically displays the folder structure of a drive or path."; ver$
PRINT
PRINT "lbbTree [drive:][path] [/A] [/C] [/F] [/M] [/O] [/S filter] [/T] [/?]"
PRINT
PRINT " /A Use ASCII instead of extended characters."
PRINT " /C Use alternative character set."
PRINT " /F Display the names of the files in each folder."
PRINT " /L Use less spacing for the margins."
PRINT " /M Display more folder/file information."
PRINT " /O Display only folders that contain files."
PRINT " /S Search for file names matching filter. */? are allowed."
PRINT " /T Truncate display to 79 charaters per line."
PRINT " /? Display this help screen."
END
END SUB