www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Is there a std.zip.ZipArchive isDir or isFile method?

reply mark <mark qtrac.eu> writes:
I'm using std.zip.ZipArchive to read zip files, e.g.:

     auto zip = new ZipArchive(read(filename));
     // ...
     foreach (name, member; zip.directory) {
	if (name.endsWith('/')) // skip dirs
	    continue;
	mkdirRecurse(dirName(name));
	zip.expand(member);
	write(name, member.expandedData());
     }

As you can see, I am detecting directories with a crude test.

I really wish there was a method for this: and if there is, could 
you give me the link 'cos I can't see one in the docs?

(BTW The code above is slightly simplified: the real code won't 
unzip if there's an absolute path or .. present and also ensures 
that all members are unzipped into a subdir even if the zip has 
top-level names.)
Feb 03 2020
next sibling parent JN <666total wp.pl> writes:
On Monday, 3 February 2020 at 13:26:38 UTC, mark wrote:
 I'm using std.zip.ZipArchive to read zip files, e.g.:

     auto zip = new ZipArchive(read(filename));
     // ...
     foreach (name, member; zip.directory) {
 	if (name.endsWith('/')) // skip dirs
 	    continue;
 	mkdirRecurse(dirName(name));
 	zip.expand(member);
 	write(name, member.expandedData());
     }

 As you can see, I am detecting directories with a crude test.

 I really wish there was a method for this: and if there is, 
 could you give me the link 'cos I can't see one in the docs?

 (BTW The code above is slightly simplified: the real code won't 
 unzip if there's an absolute path or .. present and also 
 ensures that all members are unzipped into a subdir even if the 
 zip has top-level names.)
ArchiveMember has "flags" field, perhaps it stores if it's a directory? If not, fileAttributes will have it but it looks it's OS specific.
Feb 03 2020
prev sibling parent reply cc <cc nevernet.com> writes:
On Monday, 3 February 2020 at 13:26:38 UTC, mark wrote:
 I'm using std.zip.ZipArchive to read zip files, e.g.:

     auto zip = new ZipArchive(read(filename));
     // ...
     foreach (name, member; zip.directory) {
 	if (name.endsWith('/')) // skip dirs
 	    continue;
 	mkdirRecurse(dirName(name));
 	zip.expand(member);
 	write(name, member.expandedData());
     }

 As you can see, I am detecting directories with a crude test.

 I really wish there was a method for this: and if there is, 
 could you give me the link 'cos I can't see one in the docs?

 (BTW The code above is slightly simplified: the real code won't 
 unzip if there's an absolute path or .. present and also 
 ensures that all members are unzipped into a subdir even if the 
 zip has top-level names.)
I couldn't find one either, I had to do this: version(Windows) { enum uint FILE_ATTRIBUTE_DIRECTORY = 0x10; } auto zip = new ZipArchive(buffer); foreach (fn, am; zip.directory) { if (am.fileAttributes & FILE_ATTRIBUTE_DIRECTORY) ... is directory else ... is file } As I'm looking at my code for this I'm also reminded that different zip files can internally store path separators as either \ or / depending on the platform that created them so you may need to be careful about that too. I have a bit for this that simply does: version(StandardizePathSeparators) { string filename = fn.replace("\\", "/"); } else { string filename = fn; }
Feb 11 2020
parent reply mark <mark qtrac.eu> writes:
On Wednesday, 12 February 2020 at 05:59:53 UTC, cc wrote:
 On Monday, 3 February 2020 at 13:26:38 UTC, mark wrote:
 I'm using std.zip.ZipArchive to read zip files, e.g.:
[snip]
 I couldn't find one either, I had to do this:

 version(Windows) {
 	enum uint FILE_ATTRIBUTE_DIRECTORY = 0x10;
 }
 auto zip = new ZipArchive(buffer);
 foreach (fn, am; zip.directory) {
 	if (am.fileAttributes & FILE_ATTRIBUTE_DIRECTORY)
 		... is directory
 	else
 		... is file
 }
I need to work on both Linux and Windows, and on Linux am.fileAttributes seems to be 0 for both files and directories. The lack of a cross-platform way of distinguishing whether an archive member is a directory or file does seem to be a missing piece of the API.
 As I'm looking at my code for this I'm also reminded that 
 different zip files can internally store path separators as 
 either \ or / depending on the platform that created them so 
 you may need to be careful about that too.  I have a bit for 
 this that simply does:
 version(StandardizePathSeparators) {
 	string filename = fn.replace("\\", "/");
 } else {
 	string filename = fn;
 }
Yes, I was aware of that, but I use: string filename = fn.tr("\\", "/"); // tr is from std.string
Feb 11 2020
parent cc <cc nevernet.com> writes:
It looks like 0040000 (octal) is the flag for directories on 
linux, but it does seem that std.zip is explicitly returning 0 if 
the file was created on the opposite platform re: Posix vs 
Windows, which is... odd.

 property  nogc nothrow uint fileAttributes() const
{
     version (Posix)
     {
         if ((_madeVersion & 0xFF00) == 0x0300)
             return _externalAttributes >> 16;
         return 0;
     }
     else version (Windows)
     {
         if ((_madeVersion & 0xFF00) == 0x0000)
             return _externalAttributes;
         return 0;
     }
     else
     {
         static assert(0, "Unimplemented platform");
     }
}

Looks like the only way around it is modifying std.zip?  Adding 
something like:

 property bool isDir() const {
	enum uint FILE_ATTRIBUTE_DIRECTORY = 0x10; // WINNT.h
	enum uint S_IFDIR = 0x4000; // sys/stat.h
	version(Windows) {
		if ((_madeVersion & 0xFF00) == 0x0300) // Archive made on Posix
			return cast(bool) (_externalAttributes & (S_IFDIR << 16));
		return cast(bool) (_externalAttributes & 
FILE_ATTRIBUTE_DIRECTORY);
	} else version(Posix) {
		if ((_madeVersion & 0xFF00) == 0x0300) // Archive made on Posix
			return cast(bool) (_externalAttributes & (S_IFDIR << 16));
		return cast(bool) ((_externalAttributes) & 
FILE_ATTRIBUTE_DIRECTORY);
	} else {
		static assert(0, "Unimplemented platform");
	}
}

will let me do this:

void main() {
	foreach (zipfile; ["windowstest.zip", "linuxtest.zip"]) {
		writeln(zipfile);
		auto zip = new ZipArchive(std.file.read(zipfile));
		foreach (fn, am; zip.directory) {
			writefln("%24s  %5s  %s", fn, am.isDir, am.fileAttributes);
		}
	}
}

Results on Windows:
windowstest.zip
                    a.txt  false  32
                 testdir/   true  16
            testdir/b.txt  false  32
linuxtest.zip
                    a.txt  false  0
                 testdir/   true  0
            testdir/b.txt  false  0

Results on Linux:
windowstest.zip
                 testdir/   true  0
            testdir/b.txt  false  0
                    a.txt  false  0
linuxtest.zip
                 testdir/   true  16893
            testdir/b.txt  false  33204
                    a.txt  false  33204
Feb 12 2020