Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations TouchToneTommy on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Recursively list contents of folder structure, including zip archives?

Status
Not open for further replies.

GriffMG

Programmer
Mar 4, 2002
6,305
FR
Has anyone done this in VFP?

I have a routine that can gather the names of all the files in a folder structure, but not recursively open any zip files and search them too...

Regards

Griff
Keep [Smile]ing

There are 10 kinds of people in the world, those who understand binary and those who don't.

I'm trying to cut down on the use of shrieks (exclamation marks), I'm told they are !good for you.
 
VFPCompression FLL - look at UnzipAFileInfoByIndex() or possibly UnzipAFileInfo()

The above is a site from one of our Tek-Tips members, the below seem to use Active-X controls but may also be helpful.



Also, a roundabout method might be to run UNZIP -l to list the zip file's contents redirected to a file and then read the contents with FILETOSTR() and parse through the text contents.
 
Hi

I can do that much, I was looking for something that would do it recursively, i.e. list the contents of zips within zips, possibly within zips (as far back as needed - say 10 levels)

Regards

Griff
Keep [Smile]ing

There are 10 kinds of people in the world, those who understand binary and those who don't.

I'm trying to cut down on the use of shrieks (exclamation marks), I'm told they are !good for you.
 
I would expect very few zips of zips. If a zip contains a folder structure, then most probably because a folder with subfolders was zipped, that doesn't cause zip files in a zip, just a TOC having subdirectories.

Anyway, zips can be zipped, and to get at their TOC I don't think you can get at their TOC without unzipping them from the main zip to get a zip file to act on.

If anything is capable to drill down without unzipping, it's the nowadays built in Windows functionality also allowing you to enter a zip as if it was a directory in the first place. It's the mechanism that is used for unzipping via the Shell.Application, for example in faq184-5113

Code:
o=CREATEOBJECT("shell.application")
FOR EACH ofile IN o.NameSpace(cFileZip).items
    o.NameSpace(cDestination).copyhere(ofile)
ENDFOR

This unzips by handling a zip file as a namespace (folder) and copies out files from this namespace to an outside target directory. Instead of copyhere() you could probably use oFile.Name as a namespace within the namespace and iterate all its items to get the recursion. You wouldn't do copyhere at all, since you don't want an don't need to unzip, just list files, so I guess ofile.name would be the property of the filename found, maybe including the original (relative) path. You'd need to SET STEP ON there and examine ofile with intellisense, for example. Or google shell application namespace class members and properties to know the object model. This way you simply make use of shell application know how to read a zips meta data info.

Bye, Olaf.
 
Hi Olaf

The user has received a folder which contains about 6 zip files, each of those zip files also contains four or more zip files and within each of those - going down about ten levels, there are more zip files.

They want a list of all the individual files contained within the whole structure, so they can search that list for xyz.pdf or what ever and find it in MyFile/zip1lev1.zip/zip1lev2.zip/.../zip9lev6/xyz.pdf

If the whole structure was native, I could find all the xyz.pdf files pretty easily, but because they are nested in zips in zips... It seems to be harder.

That said, your prompt may help me, thank you... off to look at shell.application



Regards

Griff
Keep [Smile]ing

There are 10 kinds of people in the world, those who understand binary and those who don't.

I'm trying to cut down on the use of shrieks (exclamation marks), I'm told they are !good for you.
 
Code:
Local objZip, objItem
objZip = Createobject("XStandard.Zip")
objItems = objZip.Contents("C:\Temp\images.zip")
For Each objItem In objItems
  ? objItem.Name
Next
objZip = .Null.
objItem = .Null.



Mike Gagnon

If you want to get the best response to a question, please check out FAQ184-2483 first.
 
Hi Mike

That looks good for opening a zip file, but the zip within the zip file.

Olaf,

I thought I had it for a minute using your suggestion, but that doesn't like opening the zip within the zip
natively.

I think I'll have to write something that extracts everything (including zips) recursively and then lists it...

Regards

Griff
Keep [Smile]ing

There are 10 kinds of people in the world, those who understand binary and those who don't.

I'm trying to cut down on the use of shrieks (exclamation marks), I'm told they are !good for you.
 
I also just made a simpler test, creating a small zip, putting that into a folder and zipping that folder.

What Shell.Application accepts as Namespace path is D:\some.zip\zippedfolder\inner.zip, but the item count of that is 0.

I would also suggest you better simply unzip all this including the zips you get from the first level unzipping and then handle it on the normal file level. It makes no sense to keep everything zipped just to save disc space. If it matters and you can't get a new external drive fast enough, you might create a folder and let it be a compressed folder via its extended properties.

Bye, Olaf.
 
I have it 50% there.

Windows needs to extract the contents of one folder to determine the one below it.

I'll post the answer tomorrow

Regards

Griff
Keep [Smile]ing

There are 10 kinds of people in the world, those who understand binary and those who don't.

I'm trying to cut down on the use of shrieks (exclamation marks), I'm told they are !good for you.
 
I have written a short piece of foul code that does the job... not very elegant, but functional

Code:
CLEAR

m.STARTFOLDER = GETDIR("H:\MERIDIAN\","Select Folder")

IF !EMPTY(m.STARTFOLDER)
	IF FILE(M.STARTFOLDER+"\SCANFOLDERLIST.TXT")
		DELETE FILE (M.STARTFOLDER+"\SCANFOLDERLIST.TXT")
	ENDIF

	SET ALTERNATE TO (M.STARTFOLDER+"\SCANFOLDERLIST.TXT")
	SET ALTERNATE ON
	? M.STARTFOLDER
	GETFILENAMES(m.STARTFOLDER,0,"")
	? "Done"
	SET ALTERNATE OFF
	SET ALTERNATE TO

ENDIF

FUNCTION GETFILENAMES
	PARAMETERS m.FILEPATH,m.LEVEL,m.PARNAME
	PRIVATE m.FILEPATH,O,m.LEVEL,m.PARNAME
	IF RIGHT(m.FILEPATH,1) <> "\"
		m.FILEPATH = m.FILEPATH+"\"
	ENDIF
	m.LEVEL = m.LEVEL +1
	O=CREATEOBJECT("shell.application")
	FOR EACH OFILE IN O.NAMESPACE(m.FILEPATH).ITEMS
		IF EMPTY(m.PARNAME)
			? m.FILEPATH+OFILE.NAME , OFILE.TYPE
		ELSE
			? m.PARNAME+OFILE.NAME, OFILE.TYPE

		ENDIF

		IF UPPER(RIGHT(OFILE.NAME,4))=".ZIP"
			M.LEVEL = M.LEVEL +1
			m.ZIPLOC = GETENV("temp")+"\zipcontents"+ALLTRIM(STR(m.LEVEL))+"\"
			IF !DIRECTORY(m.ZIPLOC)
				MD (m.ZIPLOC)
			ENDIF
			IF FILE(m.ZIPLOC+OFILE.NAME)
				DELETE FILE (m.ZIPLOC+OFILE.NAME)
			ENDIF
			O.NAMESPACE(m.ZIPLOC).COPYHERE(OFILE)
			GETZIPFILENAMES(m.ZIPLOC+OFILE.NAME,m.LEVEL,m.PARNAME+OFILE.NAME+"\",OFILE)
			IF FILE(m.ZIPLOC+OFILE.NAME)
				DELETE FILE (m.ZIPLOC+OFILE.NAME)
			ENDIF
			ZAPFOLDER(m.ZIPLOC)
		ELSE
			IF UPPER(OFILE.TYPE) = "FILE FOLDER"
				GETFILENAMES(m.FILEPATH+OFILE.NAME+"\",m.LEVEL,m.PARNAME)
			ENDIF
		ENDIF

	ENDFOR
ENDFUNC

FUNCTION GETZIPFILENAMES
	PARAMETERS m.FILEPATH,m.LEVEL,m.PARNAME,OFILE
	PRIVATE m.FILEPATH,O,m.LEVEL,m.PARNAME,OFILE
	M.LEVEL = M.LEVEL +1
	IF LEFT(M.PARNAME,LEN(M.STARTFOLDER)) <> M.STARTFOLDER
		M.PARNAME = M.STARTFOLDER+M.PARNAME
	ENDIF
	GETFILENAMES(m.FILEPATH,m.LEVEL,m.PARNAME)

ENDFUNC

FUNCTION ZAPFOLDER
	PARAMETERS m.ZAPPATH
	PRIVATE m.ZAPPATH,INSTRUCTION
	INSTRUCTION = 'RD "'+M.ZAPPATH+'" /S /Q'
	RUN &INSTRUCTION

ENDFUNC

For a time at least - you can download an install from here:


Regards

Griff
Keep [Smile]ing

There are 10 kinds of people in the world, those who understand binary and those who don't.

I'm trying to cut down on the use of shrieks (exclamation marks), I'm told they are !good for you.
 
Just one minor thing, you can avoid using RUN RD here, as VFP has RD or RMDIR as native command.
As you also use MD to create the temp folder, you can use RD in the same manner to delete it later.

Besides that PARAMETERS vs LPARAMETERS creates private variables for the parameters, so that would also be sufficient to stop the previous private variables with same name to "bleed in". Anyway, it's better to have local only variables per recursion level, so just use LPARAMETERS and no PRIVATE vars. Creating private variables only makes sense, if you want them to be accessible in multiple stack/recursion levels without needing to pass them on: see here:

Code:
RELEASE ALL EXTENDED
* creating a private variable
pCountCalls = 0
Recurse(0)

PROCEDURE Recurse()
PARAMETERS pPara1
pCountCalls = pCountCalls + 1 
? AStackInfo(laDummy)-1, pCountCalls, pPara1
If pPara1<5
   Recurse(pPara1+1)
   ? "returned from call:", pCountCalls, pPara1
ENDIF

The stack level builds up (and pCountCall reflects the same number as atackinfo rows), then when pPara reaches 5 there is no further Recurse call and the stack is reduced, the second ? command shows pPara1 at each stack level did not change and so the values go back to 4,3,2,1,0, as they were before calling, though there is no PRIVATE pPara1, only PARAMETERS pPara1.

The private variable pCountCalls created before the initial Recurse call is staying at the count level, it is also never again "declared", it is used as a public variable never needed to pass on, because that's what private variables really are.

And why did I put declared in quotes? Private variables are resulting from NOT being declared, PRIVATE is not a declaration command, it rather ends the scope of a previous variable of same name. If it would be a declaration command creating variables in the manner LOCAL does, you wouldn't have PRIVATE ALL LIKE p*, this is just defining some kind of namespace of variables that should not bleed in and therefore be available to this method/procedure to create new variables with these names or name patterns. Put in a PRIVATE pCountCalls and the line pCountCalls = pCountCalls + 1 will fail, as that mainly only undeclares the pCountCalls variables previoulsy created and initialized to 0.

So in short, in this code both pCountCalls and pPara are private, but the scope of pCountCalls is all code including recursive calls, while each Recursion() procedure recreates a pPara1 variable and so each recursion level hads its own "private" local pPara1. PARAMETERS unlike PRIVATE does not only end the scope of an eventually previously existing private variable of same name, it also then directly set the new private variable to the passed in value.

Bye, Olaf.
 
If you use the native RD, VFP can throw errors, and I didn't want any error management.

I'm sure you are right about the use of local as opposed to private vars, but it make no difference in this case.



Regards

Griff
Keep [Smile]ing

There are 10 kinds of people in the world, those who understand binary and those who don't.

I'm trying to cut down on the use of shrieks (exclamation marks), I'm told they are !good for you.
 
In regard to preventing an error of RD: Are you so unsure you leave a file in a directory, so RD fails? You may just put it into TRY..CATCH.
If you can't use a newer VFP version, there's ON ERROR * and if you really just want an external process to fail, you could take this and compile it to a vfprd.exe you run via RUN /N vfprd "&ZAPPATH" and also have no error in your exe:

Code:
LPARAMETERS tcDir
ON ERROR *
RD (tcDir)

Bye, Olaf.
 
You know I've never seen ON ERROR * before... I'll look that up

Regards

Griff
Keep [Smile]ing

There are 10 kinds of people in the world, those who understand binary and those who don't.

I'm trying to cut down on the use of shrieks (exclamation marks), I'm told they are !good for you.
 
You have seen ON ERROR DO something before, haven't you? And you've seen a comment before, haven't you?

Bye, Olaf.
 
Very good, always finding things out about VFP

Regards

Griff
Keep [Smile]ing

There are 10 kinds of people in the world, those who understand binary and those who don't.

I'm trying to cut down on the use of shrieks (exclamation marks), I'm told they are !good for you.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top