Skip to content

Added checks for volume files#38

Merged
abbeycode merged 67 commits intoabbeycode:multivolumefrom
aonez:master
Oct 2, 2017
Merged

Added checks for volume files#38
abbeycode merged 67 commits intoabbeycode:multivolumefrom
aonez:master

Conversation

@aonez
Copy link
Contributor

@aonez aonez commented Jan 9, 2017

From pull #36

This one contains various functions to check wether the file is a parted RAR volume, the get the first part path and to get the list of all the parts paths of the volume.

@aonez
Copy link
Contributor Author

aonez commented Jan 9, 2017

Here a sample code using some of the functions:

NSError *archiveError = nil;
URKArchive *archive = [[URKArchive alloc] initWithPath:rar error:&archiveError];
NSError *error = nil;

if (archive.isVolume) {
	NSLog(@"isVolume");
	NSLog(@"First volume: %@", [archive firstVolumePath]);
            
	URKArchive *volarchive = [[URKArchive alloc] initWithPath:[archive firstVolumePath] error:&archiveError];
	NSArray<NSString*> *paths = [volarchive listVolumePaths:&error];
	NSLog(@"List of volumes:");

	for (NSString *path in paths) {
		NSLog(@"%@", path);
	}
            
}
else
	NSLog(@"NOT isVolume");

Even if isVolume returns false, the listVolumePaths can return the list of volumes, if any.

@aonez
Copy link
Contributor Author

aonez commented Jan 9, 2017

Just noticed the code does not return true when asked if it is a volume if it's called with the first part. (.rar or part1.rar). The flag (MHD_VOLUME) used to detect if it is a volume returns false with that first volume.

Those flags might be needed, but are always returning false for the first volume part:
MHD_FIRSTVOLUME
LHD_SPLIT_AFTER

@abbeycode
Copy link
Owner

Thanks for keeping up on this! It looks like it'll make a good addition to the library. I'll review as soon as I'm able to.

@abbeycode
Copy link
Owner

abbeycode commented Jan 13, 2017

I'm starting to take a look at this now. To give me some context, can you explain how your code is using multi-volume archives, and how each of the methods you defined fits into your use case?

Also, have you looked at the RAR spec? It is helpful in figuring out the right way to approach problems like these.

http://www.rarlab.com/technote.htm

@aonez
Copy link
Contributor Author

aonez commented Jan 15, 2017

I needed a way to list all the parts of a volume, so I can delete them all after an extraction. Also it is useful when a user tries to extract a volume using all the parts, I can select the first one and dismiss the others.

First, this one tells me if the file is a volume part, so I can search for the first one and list all the parts. I'll try 0x0010 flag to detect the first volume (thanks for the technote tip):

- (BOOL)isVolume:(NSURL *)fileURL

If it is a volume, I want to be sure it is the first part. With this function I'll get the first volume path:

- (NSString *)firstVolumePath:(NSString *)filePath

Then, I can just extract the file using the first path. Also, I can get all the volume paths from the first one, so I can delete them or dismiss them from the extraction process:

- (NSArray<NSString*> *)listVolumePaths:(NSError **)error

This last function maybe can be done in another way. Right now I list the contents of the file so I can get the paths of all (found) volumes. Maybe it can be done using 0x0008 and 0x0010 flags and wishing the previous/next volume path and checking for it.

@aonez
Copy link
Contributor Author

aonez commented Jan 15, 2017

I'll be using it here: aonez/Keka#8

@aonez
Copy link
Contributor Author

aonez commented Jan 15, 2017

Checked again, maybe I can't get the volume info in the header because I'm using RARHeaderDataEx instead of RARHeaderData. Just checked RARHeaderData gets the flags from RARHeaderDataEx.

@abbeycode
Copy link
Owner

Sorry it took me a while to get back to you, but I was getting the code all cleaned up to work with the latest Xcode version, and it took longer than expected. That's great you found a better way to list the volume paths! Can you push an updated commit?

Also, I'm curious about why you need the - (BOOL)isVolume:(NSURL *)fileURL and - (NSString *)firstVolumePath:(NSString *)filePath overloads. When are the simpler - (BOOL)isVolume and - (NSString *)firstVolumePath not sufficient?

I'll try to keep up on this a little more to get it out the door. I'm going to be pushing a branch I created that'll help us get started on unit tests for these new methods. I'm using the rar CLI tool to create some multi-volume archives and run tests on them. I'll post more when I'm ready for you to look at them and integrate them into your PR.

@aonez
Copy link
Contributor Author

aonez commented Feb 5, 2017

@abbeycode not hurry at all. Sadly I did not found any other way to get the first part of the volume detected as a volume, nor get a consistent way to detect the next/previous volume existence. So right now the code is as here.

The overloads are not necessary for me. I'm using with the opened archive (self.filename), but I think maybe someone will need to pass another path, and why not coding it already this capability. If you like I can change it to use always self.filename and get rid of the path overload, no problem at all.

Let me know how the tests go! Maybe you get motivated and find the right flag to get the first volume path detected ;)

@aonez
Copy link
Contributor Author

aonez commented Feb 5, 2017

Just rebased with your master, seems there're too many changes now in the pull request...

@abbeycode
Copy link
Owner

That's strange that it's showing all of those as changes. Did you do a git rebase, or did you git merge?

@aonez
Copy link
Contributor Author

aonez commented Feb 5, 2017

Did a rebase but using the Fork GUI. First rebase for me by the way, so not surprised it got broken...

@abbeycode
Copy link
Owner

Interesting... I think it messed it up. Also, regardless of what it did, I think GitHub expects you to just merge, not rebase. I've run into it before. Can you try doing a hard reset of your branch to a previous revision and do it over?

@aonez
Copy link
Contributor Author

aonez commented Feb 5, 2017 via email

@aonez
Copy link
Contributor Author

aonez commented Feb 5, 2017

Done! Hard reset and a merge did it.

abbeycode added a commit that referenced this pull request Feb 9, 2017
@abbeycode
Copy link
Owner

I added the tests and stubbed out methods. I renamed the one method to be a little clearer, and added NSURL-based overloads and documentation.

Also, while I was running the tests with your current implementation, it looks like you're going to want to use this in your isVolume check:

bool isVolume = (flags->Flags & MHD_VOLUME) != 0;

RARReadHeaderEx() reads file headers, not the archive header.

Let me know if you have any other questions! Also, you should probably update the PR to merge into that new multivolume branch I created instead of master. I'll merge it in from there.

@aonez
Copy link
Contributor Author

aonez commented Feb 10, 2017

You got it, using flags the first volume is detected. I'll spend some time next week to merge it into the branch and take a look at all the test and modifications you made. Thanks @abbeycode!

@aonez
Copy link
Contributor Author

aonez commented Feb 26, 2017

Little busy right now, but I'll get to it!

@abbeycode
Copy link
Owner

abbeycode commented Aug 16, 2017

Hello. It's been a while, and I was wondering if you were still planning on pushing this PR over the finish line. If so, let me know and I'll bring the multivolume branch up to date with the v2.9 branch, and keep it up to date.

@aonez
Copy link
Contributor Author

aonez commented Aug 17, 2017

Just was thinking about this some days ago. Sure I will!

@aonez aonez changed the base branch from master to multivolume August 17, 2017 09:37
@aonez
Copy link
Contributor Author

aonez commented Aug 17, 2017

Ok, so my PR is updated to the current status of the multivolume branch. Let me know when you update it with v2.9changes so I'll update it again 😅

@abbeycode
Copy link
Owner

I updated the multivolume branch. Let's make sure the Travis build completes successfully before you merge. I'll send another note when it looks good.

@abbeycode
Copy link
Owner

The build failed, but that's because of the unit tests that this PR will fix, so you may proceed!

@aonez
Copy link
Contributor Author

aonez commented Aug 17, 2017

Should work now, let's see what travis says 😛

@abbeycode
Copy link
Owner

Hmmmm still looks like the new unit tests are failing...

@aonez
Copy link
Contributor Author

aonez commented Aug 18, 2017 via email

@aonez
Copy link
Contributor Author

aonez commented Aug 18, 2017

Cleaned all my code and failed again. Then I saw your branch failed too. Look at your latest commit travis CI status.

I'm mostly sure this is why it failed yesterday. I'm not familiar with travis CI, maybe you'll see whats the problem there.

Copy link
Contributor Author

@aonez aonez left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fillet = Filled 😑

Copy link
Owner

@abbeycode abbeycode left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for picking this back up! I'm excited to include this new functionality in the library. I have one incredibly minor nitpick, and one more substantial issue. Please take a look when you can, and we'll get this over the finish line.

return false;
}

return (volumeURLs.count > 1);
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very very minor suggestion: No need for the parentheses here.

@try {
NSError *firstVolumeError = nil;

if ([self _unrarOpenFile:firstVolumeURL.path
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please take a look at this suggestion. It's the only real thing holding this back. Thanks for hopping back on this request!

* @return Returns the URL, or nil if there was an error
*/
- (nullable NSURL *)firstVolumeURL;
- (NSURL *)firstVolumeURL:(NSURL *)fileURL;
Copy link
Contributor Author

@aonez aonez Sep 29, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Those will be not nullable, since they will be called after a (!fileURL) check. If no first part, they will return the passed fileURL.

return false;
}

return volumeURLs.count > 1;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No extra parenthesis 👍


- (nullable NSURL *)firstVolumeURL {
NSString *path = [self firstVolumePath];
- (NSURL *)firstVolumeURL:(NSURL *)fileURL {
Copy link
Contributor Author

@aonez aonez Sep 29, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed the contents to the NSURL method because init uses NSURL

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a few thoughts on this.

  1. Since we're working with regular expressions in the firstVolume... method, it makes sense for it to use a path. Let it be true to itself
  2. I think it would be clearer if firstVolumePath were a class method that takes a path, since it isn't using any internal state of the class
  3. It only needs to be private. We can get rid of the public firstVolumeURL and firstPathURL instance methods, since all instances are going to represent the first volume. Basically, it will always be true that firstVolumeURL == fileURL

return [NSURL fileURLWithPath:volumePath];
}
else
URKLogInfo("First volume part %@ not found. Skipping first volume selection", volumePath);
Copy link
Contributor Author

@aonez aonez Sep 29, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just skip, do not return nil. URKLogInfo, this will get to the final user?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, all logging is for the dev to see, not the user.

Copy link
Owner

@abbeycode abbeycode Sep 29, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, look at the README for logging suggestions. This should probably be Debug level

URKLogInfo("First volume part %@ not found. Skipping first volume selection", volumePath);
}

return fileURL;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If no match, always return the passed fileURL

return @[];
NSMutableOrderedSet<NSString*> *volumePaths = [NSMutableOrderedSet new];

NSArray<URKFileInfo*> *listFileInfo = [self listFileInfo:error];
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Way cleaner 😃

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The NSMutableOrderedSet class doesn't sort members as they're added. You're better off using a regular NSMutableSet and sorting it when you convert to an array.

}


fileURL = [self firstVolumeURL:fileURL];
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just added it before (!fileURL) check and [self init], let me know if you think there's a better place


if (![path length]) {
return nil;
URKLogDebug("Checking if the archive is part of a volume...");
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added URKLogDebug trough all the process

@aonez
Copy link
Contributor Author

aonez commented Sep 29, 2017

If the changes are ok for you, the case tests calls to [archive firstVolumePath] and [archive firstVolumeURL] should be replaced to [archive filename] and [archive fileURL].

Alse we need to determine what to do with this check:

- (void)testMultipleVolume_FirstVolumeMissing;

Maybe add an overload to firstVolumeURL to return nil if not found so the developer can forcibly ask for it?

Copy link
Owner

@abbeycode abbeycode left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I appreciate all of your work on this. I still feel like there's some work to be done, but I'll leave it up to you whether you want to continue going through the PR feedback loop or not. I'm fine will pulling your changes into the multivolume branch at this point and making the changes I'm suggesting myself. If you feel like you're getting something out of the review process, though, then we can keep going and bring it over the finish line together. It's your call.


- (nullable NSURL *)firstVolumeURL {
NSString *path = [self firstVolumePath];
- (NSURL *)firstVolumeURL:(NSURL *)fileURL {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a few thoughts on this.

  1. Since we're working with regular expressions in the firstVolume... method, it makes sense for it to use a path. Let it be true to itself
  2. I think it would be clearer if firstVolumePath were a class method that takes a path, since it isn't using any internal state of the class
  3. It only needs to be private. We can get rid of the public firstVolumeURL and firstPathURL instance methods, since all instances are going to represent the first volume. Basically, it will always be true that firstVolumeURL == fileURL

return nil;
URKLogDebug("Checking if the archive is part of a volume...");

__block NSString *volumePath = fileURL.path;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't need to be a __block variable, since it's not modified from inside a block. Also, once we refactor to a single class method (+[URKArchive firstVolumePath:]), it'll be the input argument.

__block NSString *volumePath = fileURL.path;
NSTextCheckingResult * match;

if (volumePath.length)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Invert this if and exit early. If volumePath is empty, log an error and return nil. That way we un-nest everything below, and you don't need to check for match twice. You can simplify the rest of the method's conditions.

return @[];
NSMutableOrderedSet<NSString*> *volumePaths = [NSMutableOrderedSet new];

NSArray<URKFileInfo*> *listFileInfo = [self listFileInfo:error];
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The NSMutableOrderedSet class doesn't sort members as they're added. You're better off using a regular NSMutableSet and sorting it when you convert to an array.

@aonez
Copy link
Contributor Author

aonez commented Sep 30, 2017

Go ahead with the pull. I think now all the elements are working, it's just a matter of "coding" preferences and optimisations that maybe you'll do faster. I'll take a look at the final code once is there 🤓 Obviously if you need anything from my part just ask.

@abbeycode
Copy link
Owner

@aonez Thanks again for all your work on this! I'll mention you in the PR I create to merge the multivolume branch into my release branch (v2.9).

@abbeycode abbeycode merged commit d129c73 into abbeycode:multivolume Oct 2, 2017
@abbeycode
Copy link
Owner

@aonez Take a look at the PR I created (linked above) and let me know what you think. If it looks good, I'll merge to v2.9 and release a second beta soon.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants