1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
|
#import "XADArParser.h"
@implementation XADArParser
+(int)requiredHeaderSize { return 6; }
+(BOOL)recognizeFileWithHandle:(CSHandle *)handle firstBytes:(NSData *)data name:(NSString *)name
{
const uint8_t *bytes=[data bytes];
int length=[data length];
if(length<8) return NO;
return memcmp(bytes,"!<arch>\n",8)==0;
}
static uint64_t ParseDecimal(const uint8_t *ptr,int maxlen)
{
uint64_t val=0;
for(int i=0;i<maxlen;i++)
{
if(*ptr<'0'||*ptr>'9') break;
val=val*10+*ptr-'0';
ptr++;
}
return val;
}
static uint64_t ParseOctal(const uint8_t *ptr,int maxlen)
{
uint64_t val=0;
for(int i=0;i<maxlen;i++)
{
if(*ptr<'0'||*ptr>'7') break;
val=val*8+*ptr-'0';
ptr++;
}
return val;
}
-(void)parse
{
CSHandle *fh=[self handle];
[fh skipBytes:8];
NSData *filenametable=nil; // TODO: Maybe this shouldn't be autoreleased.
while(![fh atEndOfFile] && [self shouldKeepParsing])
{
uint8_t header[60];
[fh readBytes:sizeof(header) toBuffer:header];
if(header[58]!=0x60||header[59]!=0x0a) [XADException raiseIllegalDataException];
uint64_t timestamp=ParseDecimal(&header[16],12);
int owner=(int)ParseDecimal(&header[28],6);
int group=(int)ParseDecimal(&header[34],6);
int mode=(int)ParseOctal(&header[40],8);
uint64_t size=ParseDecimal(&header[48],10);
XADPath *name;
if(header[0]=='#'&&header[1]=='1'&&header[2]=='/')
{
// BSD long filename.
int namelen=(int)ParseDecimal(&header[3],12);
uint8_t namebuf[namelen];
[fh readBytes:namelen toBuffer:namebuf];
size-=namelen;
while(namelen && namebuf[namelen-1]==0) namelen--;
name=[self XADPathWithBytes:namebuf length:namelen separators:XADNoPathSeparator];
}
else if(header[0]=='/'&&header[1]==' ')
{
// GNU symbol table, ignore.
[fh skipBytes:size];
continue;
}
else if(header[0]=='/'&&header[1]=='/'&&header[2]==' ')
{
// GNU long filename list.
filenametable=[fh readDataOfLength:(int)size];
continue;
}
else if(header[0]=='/'&&header[1]>='0'&&header[1]<='9')
{
// GNU long filename.
int nameoffs=(int)ParseDecimal(&header[1],14);
const uint8_t *tablebytes=[filenametable bytes];
int tablelength=[filenametable length];
if(nameoffs>=tablelength) [XADException raiseIllegalDataException];
int endoffs=nameoffs;
while(endoffs<tablelength&&tablebytes[endoffs]!='\n'&&
tablebytes[endoffs]!='/') endoffs++;
name=[self XADPathWithBytes:&tablebytes[nameoffs] length:endoffs-nameoffs separators:XADNoPathSeparator];
}
else
{
// Regular entry.
int namelen=16;
while(namelen && (header[namelen-1]==' '||header[namelen-1]=='/'))
namelen--;
name=[self XADPathWithBytes:header length:namelen separators:XADNoPathSeparator];
}
off_t offs=[fh offsetInFile];
NSMutableDictionary *dict=[NSMutableDictionary dictionaryWithObjectsAndKeys:
name,XADFileNameKey,
[NSNumber numberWithUnsignedLongLong:size],XADFileSizeKey,
[NSNumber numberWithUnsignedLongLong:size],XADCompressedSizeKey,
[NSNumber numberWithUnsignedLongLong:size],XADDataLengthKey,
[NSNumber numberWithUnsignedLongLong:offs],XADDataOffsetKey,
[NSDate dateWithTimeIntervalSince1970:timestamp],XADLastModificationDateKey,
[NSNumber numberWithInt:owner],XADPosixUserKey,
[NSNumber numberWithInt:group],XADPosixGroupKey,
[NSNumber numberWithInt:mode],XADPosixPermissionsKey,
[self XADStringWithString:@"None"],XADCompressionNameKey,
nil];
[self addEntryWithDictionary:dict];
[fh seekToFileOffset:((offs+size+1)&~1)];
}
}
-(CSHandle *)handleForEntryWithDictionary:(NSDictionary *)dict wantChecksum:(BOOL)checksum
{
return [self handleAtDataOffsetForDictionary:dict];
}
-(NSString *)formatName { return @"Ar"; }
@end
|