XADMaster/XADArchiveParserDescriptions.m
2018-03-21 14:24:50 +02:00

523 lines
18 KiB
Objective-C

/*
* XADArchiveParserDescriptions.m
*
* Copyright (c) 2017-present, MacPaw Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA
*/
#import "XADArchiveParserDescriptions.h"
#import "XADRegex.h"
@implementation XADArchiveParser (Descriptions)
-(NSString *)descriptionOfValueInDictionary:(NSDictionary *)dict key:(NSString *)key
{
id object=[dict objectForKey:key];
if(!object) return nil;
if([key matchedByPattern:@"CRC32$"])
{
if(![object isKindOfClass:[NSNumber class]]) return [object description];
return [NSString stringWithFormat:@"0x%08x",[object unsignedIntValue]];
}
else if([key matchedByPattern:@"CRC16$"])
{
if(![object isKindOfClass:[NSNumber class]]) return [object description];
return [NSString stringWithFormat:@"0x%04x",[object unsignedIntValue]];
}
else if([key matchedByPattern:@"Is[A-Z0-9]"])
{
if(![object isKindOfClass:[NSNumber class]]) return [object description];
return XADHumanReadableBoolean([object longLongValue]);
}
else if([key isEqual:XADFileSizeKey]||[key isEqual:XADCompressedSizeKey])
{
if(![object isKindOfClass:[NSNumber class]]) return [object description];
return XADHumanReadableFileSize([object longLongValue]);
}
else if([key isEqual:XADPosixPermissionsKey])
{
if(![object isKindOfClass:[NSNumber class]]) return [object description];
return XADHumanReadablePOSIXPermissions([object longLongValue]);
}
else if([key isEqual:XADAmigaProtectionBitsKey])
{
if(![object isKindOfClass:[NSNumber class]]) return [object description];
return XADHumanReadableAmigaProtectionBits([object longLongValue]);
}
else if([key isEqual:XADDOSFileAttributesKey])
{
if(![object isKindOfClass:[NSNumber class]]) return [object description];
return XADHumanReadableDOSFileAttributes([object longLongValue]);
}
else if([key isEqual:XADWindowsFileAttributesKey])
{
if(![object isKindOfClass:[NSNumber class]]) return [object description];
return XADHumanReadableWindowsFileAttributes([object longLongValue]);
}
else if([key isEqual:XADFileTypeKey]||[key isEqual:XADFileCreatorKey])
{
if(![object isKindOfClass:[NSNumber class]]) return [object description];
return XADHumanReadableOSType([object longLongValue]);
}
else if([key isEqual:XADFinderFlagsKey])
{
if(![object isKindOfClass:[NSNumber class]]) return [object description];
return [NSString stringWithFormat:@"0x%04llx",[object longLongValue]];
}
else
{
return XADHumanReadableObject(object);
}
}
-(NSString *)descriptionOfKey:(NSString *)key
{
static NSDictionary *descriptions=nil;
if(!descriptions) descriptions=[[NSDictionary alloc] initWithObjectsAndKeys:
NSLocalizedString(@"Comment",@""),XADCommentKey,
NSLocalizedString(@"Name",@""),XADFileNameKey,
NSLocalizedString(@"Size",@""),XADFileSizeKey,
NSLocalizedString(@"Compressed size",@""),XADCompressedSizeKey,
NSLocalizedString(@"Compression type",@""),XADCompressionNameKey,
NSLocalizedString(@"Is a directory",@""),XADIsDirectoryKey,
NSLocalizedString(@"Is a Mac OS resource fork",@""),XADIsResourceForkKey,
NSLocalizedString(@"Is an archive",@""),XADIsArchiveKey,
NSLocalizedString(@"Is hidden",@""),XADIsHiddenKey,
NSLocalizedString(@"Is a link",@""),XADIsLinkKey,
NSLocalizedString(@"Is a hard link",@""),XADIsHardLinkKey,
NSLocalizedString(@"Link destination",@""),XADLinkDestinationKey,
NSLocalizedString(@"Is a Unix character device",@""),XADIsCharacterDeviceKey,
NSLocalizedString(@"Is a Unix block device",@""),XADIsBlockDeviceKey,
NSLocalizedString(@"Unix major device number",@""),XADDeviceMajorKey,
NSLocalizedString(@"Unix minor device number",@""),XADDeviceMinorKey,
NSLocalizedString(@"Is a Unix FIFO",@""),XADIsFIFOKey,
NSLocalizedString(@"Is encrypted",@""),XADIsEncryptedKey,
NSLocalizedString(@"Is corrupted",@""),XADIsCorruptedKey,
NSLocalizedString(@"Last modified",@""),XADLastModificationDateKey,
NSLocalizedString(@"Last accessed",@""),XADLastAccessDateKey,
NSLocalizedString(@"Last attribute change",@""),XADLastAttributeChangeDateKey,
NSLocalizedString(@"Last backup",@""),XADLastBackupDateKey,
NSLocalizedString(@"Created",@""),XADCreationDateKey,
NSLocalizedString(@"Extended attributes",@""),XADExtendedAttributesKey,
NSLocalizedString(@"Mac OS type code",@""),XADFileTypeKey,
NSLocalizedString(@"Mac OS creator code",@""),XADFileCreatorKey,
NSLocalizedString(@"Mac OS Finder flags",@""),XADFinderFlagsKey,
NSLocalizedString(@"Mac OS Finder info",@""),XADFinderInfoKey,
NSLocalizedString(@"Unix permissions",@""),XADPosixPermissionsKey,
NSLocalizedString(@"Unix user number",@""),XADPosixUserKey,
NSLocalizedString(@"Unix group number",@""),XADPosixGroupKey,
NSLocalizedString(@"Unix user name",@""),XADPosixUserNameKey,
NSLocalizedString(@"Unix group name",@""),XADPosixGroupNameKey,
NSLocalizedString(@"DOS file attributes",@""),XADDOSFileAttributesKey,
NSLocalizedString(@"Windows file attributes",@""),XADWindowsFileAttributesKey,
NSLocalizedString(@"Amiga protection bits",@""),XADAmigaProtectionBitsKey,
NSLocalizedString(@"Index in file",@""),XADIndexKey,
NSLocalizedString(@"Start of data",@""),XADDataOffsetKey,
NSLocalizedString(@"Length of data",@""),XADDataLengthKey,
NSLocalizedString(@"Start of data (minus skips)",@""),XADSkipOffsetKey,
NSLocalizedString(@"Length of data (minus skips)",@""),XADSkipLengthKey,
NSLocalizedString(@"Is a solid archive file",@""),XADIsSolidKey,
NSLocalizedString(@"Index of first solid file",@""),XADFirstSolidIndexKey,
NSLocalizedString(@"Pointer to first solid file",@""),XADFirstSolidEntryKey,
NSLocalizedString(@"Index of next solid file",@""),XADNextSolidIndexKey,
NSLocalizedString(@"Pointer to next solid file",@""),XADNextSolidEntryKey,
NSLocalizedString(@"Internal solid identifier",@""),XADSolidObjectKey,
NSLocalizedString(@"Start of data in solid stream",@""),XADSolidOffsetKey,
NSLocalizedString(@"Length of data in solid stream",@""),XADSolidLengthKey,
NSLocalizedString(@"Archive name",@""),XADArchiveNameKey,
NSLocalizedString(@"Archive volumes",@""),XADVolumesKey,
NSLocalizedString(@"Disk label",@""),XADDiskLabelKey,
nil];
NSString *description=[descriptions objectForKey:key];
if(description) return description;
return key;
}
static NSComparisonResult OrderKeys(id first,id second,void *context);
-(NSArray *)descriptiveOrderingOfKeysInDictionary:(NSDictionary *)dict
{
static NSDictionary *ordering=nil;
if(!ordering) ordering=[[NSDictionary alloc] initWithObjectsAndKeys:
[NSNumber numberWithInt:100],XADFileNameKey,
[NSNumber numberWithInt:101],XADCommentKey,
[NSNumber numberWithInt:102],XADFileSizeKey,
[NSNumber numberWithInt:103],XADCompressedSizeKey,
[NSNumber numberWithInt:104],XADCompressionNameKey,
[NSNumber numberWithInt:200],XADIsDirectoryKey,
[NSNumber numberWithInt:201],XADIsResourceForkKey,
[NSNumber numberWithInt:202],XADIsArchiveKey,
[NSNumber numberWithInt:203],XADIsHiddenKey,
[NSNumber numberWithInt:204],XADIsLinkKey,
[NSNumber numberWithInt:205],XADIsHardLinkKey,
[NSNumber numberWithInt:206],XADLinkDestinationKey,
[NSNumber numberWithInt:207],XADIsCharacterDeviceKey,
[NSNumber numberWithInt:208],XADIsBlockDeviceKey,
[NSNumber numberWithInt:209],XADDeviceMajorKey,
[NSNumber numberWithInt:210],XADDeviceMinorKey,
[NSNumber numberWithInt:211],XADIsFIFOKey,
[NSNumber numberWithInt:212],XADIsEncryptedKey,
[NSNumber numberWithInt:213],XADIsCorruptedKey,
[NSNumber numberWithInt:300],XADLastModificationDateKey,
[NSNumber numberWithInt:301],XADLastAccessDateKey,
[NSNumber numberWithInt:302],XADLastAttributeChangeDateKey,
[NSNumber numberWithInt:303],XADLastBackupDateKey,
[NSNumber numberWithInt:304],XADCreationDateKey,
[NSNumber numberWithInt:400],XADExtendedAttributesKey,
[NSNumber numberWithInt:401],XADFileTypeKey,
[NSNumber numberWithInt:402],XADFileCreatorKey,
[NSNumber numberWithInt:403],XADFinderFlagsKey,
[NSNumber numberWithInt:404],XADFinderInfoKey,
[NSNumber numberWithInt:404],XADPosixPermissionsKey,
[NSNumber numberWithInt:405],XADPosixUserKey,
[NSNumber numberWithInt:406],XADPosixGroupKey,
[NSNumber numberWithInt:407],XADPosixUserNameKey,
[NSNumber numberWithInt:408],XADPosixGroupNameKey,
[NSNumber numberWithInt:409],XADDOSFileAttributesKey,
[NSNumber numberWithInt:410],XADWindowsFileAttributesKey,
[NSNumber numberWithInt:411],XADAmigaProtectionBitsKey,
[NSNumber numberWithInt:500],XADIndexKey,
[NSNumber numberWithInt:501],XADDataOffsetKey,
[NSNumber numberWithInt:502],XADDataLengthKey,
[NSNumber numberWithInt:503],XADSkipOffsetKey,
[NSNumber numberWithInt:504],XADSkipLengthKey,
[NSNumber numberWithInt:600],XADIsSolidKey,
[NSNumber numberWithInt:601],XADFirstSolidIndexKey,
[NSNumber numberWithInt:602],XADFirstSolidEntryKey,
[NSNumber numberWithInt:603],XADNextSolidIndexKey,
[NSNumber numberWithInt:604],XADNextSolidEntryKey,
[NSNumber numberWithInt:605],XADSolidObjectKey,
[NSNumber numberWithInt:606],XADSolidOffsetKey,
[NSNumber numberWithInt:607],XADSolidLengthKey,
[NSNumber numberWithInt:700],XADArchiveNameKey,
[NSNumber numberWithInt:701],XADVolumesKey,
[NSNumber numberWithInt:702],XADDiskLabelKey,
nil];
return [[dict allKeys] sortedArrayUsingFunction:OrderKeys context:ordering];
}
static NSComparisonResult OrderKeys(id first,id second,void *context)
{
NSDictionary *ordering=context;
NSNumber *firstorder=[ordering objectForKey:first];
NSNumber *secondorder=[ordering objectForKey:second];
if(firstorder&&secondorder) return [firstorder compare:secondorder];
else if(firstorder) return NSOrderedAscending;
else if(secondorder) return NSOrderedDescending;
else return [first compare:second];
}
@end
static NSString *DottedNumber(uint64_t size);
NSString *XADHumanReadableFileSize(uint64_t size)
{
if(size<1000) return [NSString localizedStringWithFormat:
NSLocalizedString(@"%lld bytes",@"Format string for human-redable sizes <1000"),
size];
else return [NSString localizedStringWithFormat:
NSLocalizedString(@"%@ (%@ bytes)",@"Format string for human-readable sizes >=1000"),
XADShortHumanReadableFileSize(size),DottedNumber(size)];
}
NSString *XADShortHumanReadableFileSize(uint64_t size)
{
if(size<1000)
{
return [NSString stringWithFormat:
NSLocalizedString(@"%lld B",@"Format string for short sizes expressed in bytes"),
size];
}
double value;
NSString *unitformat;
if(size<1000000)
{
value=size/1000.0;
unitformat=NSLocalizedString(@"%@ KB",@"Format string for short sizes expressed in kilobytes");
}
else if(size<1000000000)
{
value=size/1000000.0;
unitformat=NSLocalizedString(@"%@ MB",@"Format string for short sizes expressed in megabytes");
}
else
{
value=size/1000000000.0;
unitformat=NSLocalizedString(@"%@ GB",@"Format string for short sizes expressed in gigabytes");
}
NSString *number;
if(value<10) number=[NSString localizedStringWithFormat:@"%.2f",value];
else if(value<100) number=[NSString localizedStringWithFormat:@"%.1f",value];
else number=[NSString localizedStringWithFormat:@"%.0f",value];
return [NSString stringWithFormat:unitformat,number];
}
static NSString *DottedNumber(uint64_t size)
{
NSNumberFormatter *formatter=[[NSNumberFormatter new] autorelease];
formatter.formatterBehavior=NSNumberFormatterBehavior10_4;
formatter.numberStyle=NSNumberFormatterDecimalStyle;
return [formatter stringFromNumber:[NSNumber numberWithLongLong:size]];
}
NSString *XADHumanReadableBoolean(uint64_t boolean)
{
if(boolean==1) return NSLocalizedString(@"Yes",@"String for true values");
else if (boolean==0) return NSLocalizedString(@"No",@"String for false values");
else return [NSString stringWithFormat:@"%llx",boolean];
}
NSString *XADHumanReadablePOSIXPermissions(uint64_t permissions)
{
char str[10]="rwxrwxrwx";
for(int i=0;i<9;i++) if(!(permissions&(0400>>i))) str[i]='-';
return [NSString stringWithFormat:@"%s (%llo)",str,permissions];
}
NSString *XADHumanReadableAmigaProtectionBits(uint64_t protection)
{
char str[9]="hsparwed";
for(int i=0;i<8;i++) if(!((protection^0x0f)&(0x80>>i))) str[i]='-';
return [NSString stringWithFormat:@"%s (0x%02llx)",str,protection];
}
NSString *XADHumanReadableDOSFileAttributes(uint64_t attributes)
{
char str[9]="ADVSHR";
for(int i=0;i<6;i++) if(!(attributes&(0x20>>i))) str[i]='-';
return [NSString stringWithFormat:@"%s (0x%02llx)",str,attributes];
}
NSString *XADHumanReadableWindowsFileAttributes(uint64_t attributes)
{
char str[16]="EIOCLPTN AD SHR";
for(int i=0;i<15;i++) if(!(attributes&(0x4000>>i))) str[i]='-';
return [NSString stringWithFormat:@"%s (0x%04llx)",str,attributes];
}
NSString *XADHumanReadableOSType(uint64_t ostype)
{
char str[5]={0};
for(int i=0;i<4;i++)
{
uint8_t c=(ostype>>(24-i*8))&0xff;
if(c>=32&&c<=127) str[i]=c;
else str[i]='?';
}
return [NSString stringWithFormat:@"%s (0x%08llx)",str,ostype];
}
NSString *XADHumanReadableEntryWithDictionary(NSDictionary *dict,XADArchiveParser *parser)
{
NSArray *keys=[parser descriptiveOrderingOfKeysInDictionary:dict];
NSMutableArray *labels=[NSMutableArray array];
NSMutableArray *values=[NSMutableArray array];
NSEnumerator *enumerator=[keys objectEnumerator];
NSString *key;
while((key=[enumerator nextObject]))
{
NSString *label=[parser descriptionOfKey:key];
NSString *value=[parser descriptionOfValueInDictionary:dict key:key];
[labels addObject:label];
[values addObject:value];
}
return XADHumanReadableList(labels,values);
}
#ifdef GNUSTEP
static NSString *GNUSTEPKludge_HumanReadableValue(NSValue *value);
#endif
NSString *XADHumanReadableObject(id object)
{
if ([object isKindOfClass:[NSDate class]]) return XADHumanReadableDate(object);
else if([object isKindOfClass:[NSData class]]) return XADHumanReadableData(object);
else if([object isKindOfClass:[NSArray class]]) return XADHumanReadableArray(object);
else if([object isKindOfClass:[NSDictionary class]]) return XADHumanReadableDictionary(object);
#ifdef GNUSTEP
else if([object isKindOfClass:[NSValue class]]) return GNUSTEPKludge_HumanReadableValue(object);
#endif
else return [object description];
}
NSString *XADHumanReadableDate(NSDate *date)
{
#ifndef __COCOTRON__
NSDateFormatter *formatter=[[NSDateFormatter new] autorelease];
[formatter setFormatterBehavior:NSDateFormatterBehavior10_4];
[formatter setDateStyle:NSDateFormatterFullStyle];
[formatter setTimeStyle:NSDateFormatterMediumStyle];
return [formatter stringForObjectValue:date];
#else
return [date description];
#endif
}
NSString *XADHumanReadableData(NSData *data)
{
NSMutableString *string=[NSMutableString string];
NSInteger length=[data length];
const uint8_t *bytes=[data bytes];
[string appendFormat:
NSLocalizedString(@"%llu bytes (",@"Format string for raw data objects"),
(uint64_t)length];
for(int i=0;i<length && i<256;i++)
{
if(i!=0 && (i&3)==0) [string appendString:@" "];
[string appendFormat:@"%02x",bytes[i]];
}
if(length>256) [string appendString:@"..."];
[string appendString:@")"];
return string;
}
NSString *XADHumanReadableArray(NSArray *array)
{
NSInteger count=[array count];
NSMutableArray *labels=[NSMutableArray array];
NSMutableArray *values=[NSMutableArray array];
for(int i=0;i<count;i++)
{
id value=[array objectAtIndex:i];
[labels addObject:[NSString stringWithFormat:@"%d",i]];
[values addObject:XADHumanReadableObject(value)];
}
return XADHumanReadableList(labels,values);
}
NSString *XADHumanReadableDictionary(NSDictionary *dict)
{
NSArray *keys=[[dict allKeys] sortedArrayUsingSelector:@selector(compare:)];
NSMutableArray *labels=[NSMutableArray array];
NSMutableArray *values=[NSMutableArray array];
NSEnumerator *enumerator=[keys objectEnumerator];
NSString *key;
while((key=[enumerator nextObject]))
{
id value=[dict objectForKey:key];
[labels addObject:key];
[values addObject:XADHumanReadableObject(value)];
}
return XADHumanReadableList(labels,values);
}
NSString *XADHumanReadableList(NSArray *labels,NSArray *values)
{
int maxlen=0;
NSInteger count=[labels count];
for(int i=0;i<count;i++)
{
NSString *label=[labels objectAtIndex:i];
int len=[label length];
if(len>maxlen) maxlen=len;
}
NSMutableString *string=[NSMutableString string];
for(int i=0;i<count;i++)
{
NSString *label=[labels objectAtIndex:i];
NSString *value=[values objectAtIndex:i];
int len=[label length];
[string appendString:label];
[string appendString:@": "];
for(int i=len;i<maxlen;i++) [string appendString:@" "];
[string appendString:XADIndentTextWithSpaces(value,maxlen+2)];
if(i!=count-1) [string appendString:@"\n"];
}
return string;
}
NSString *XADIndentTextWithSpaces(NSString *text,int spaces)
{
if([text rangeOfString:@"\n"].location==NSNotFound) return text;
NSMutableString *res=[NSMutableString string];
int length=[text length];
for(int i=0;i<length;i++)
{
unichar c=[text characterAtIndex:i];
[res appendFormat:@"%C",c];
if(c=='\n')
{
for(int j=0;j<spaces;j++) [res appendString:@" "];
}
}
return res;
}
#ifdef GNUSTEP
// GNUstep will dereference non-retained objects in NSValues. This is not safe, so avoid it.
static NSString *GNUSTEPKludge_HumanReadableValue(NSValue *value)
{
if(strcmp([value objCType],@encode(id))==0)
{
return [NSString stringWithFormat:@"<%p>",[value nonretainedObjectValue]];
}
else
{
return [value description];
}
}
#endif