50#if OO_OXP_VERIFIER_ENABLED
59static BOOL
CheckNameConflict(NSString *lcName, NSDictionary *directoryCases, NSDictionary *rootFiles, NSString **outExisting, NSString **outExistingType);
62@interface OOFileScannerVerifierStage (OOPrivate)
76- (NSDictionary *)lowercaseMap:(NSArray *)array;
78- (NSDictionary *)scanDirectory:(NSString *)path;
79- (void)checkPListFormat:(NSPropertyListFormat)format file:(NSString *)file folder:(NSString *)folder;
91 [_caseWarnings release];
92 [_directoryListings release];
93 [_directoryCases release];
108 NSAutoreleasePool *pool =
nil;
110 _usedFiles = [[NSMutableSet alloc] init];
111 _caseWarnings = [[NSMutableSet alloc] init];
112 _badPLists = [[NSMutableSet alloc] init];
114 pool = [[NSAutoreleasePool alloc] init];
118 pool = [[NSAutoreleasePool alloc] init];
119 [
self checkRootFolders];
120 [
self checkKnownFiles];
125+ (NSString *)nameForDependencyForVerifier:(
OOOXPVerifier *)verifier
139- (BOOL)fileExists:(NSString *)file
140 inFolder:(NSString *)folder
141 referencedFrom:(NSString *)context
142 checkBuiltIn:(BOOL)checkBuiltIn
144 return [
self pathForFile:file inFolder:folder referencedFrom:context checkBuiltIn:checkBuiltIn] !=
nil;
148- (NSString *)pathForFile:(NSString *)file
149 inFolder:(NSString *)folder
150 referencedFrom:(NSString *)context
151 checkBuiltIn:(BOOL)checkBuiltIn
153 NSString *lcName =
nil,
160 if (file ==
nil)
return nil;
161 lcName = [file lowercaseString];
165 lcDirName = [folder lowercaseString];
166 realFileName = [[_directoryListings oo_dictionaryForKey:lcDirName] objectForKey:lcName];
168 if (realFileName !=
nil)
170 realDirName = [_directoryCases objectForKey:lcDirName];
171 path = [realDirName stringByAppendingPathComponent:realFileName];
177 realFileName = [[_directoryListings oo_dictionaryForKey:@""] objectForKey:lcName];
179 if (realFileName !=
nil)
187 [_usedFiles addObject:path];
188 if (realDirName !=
nil && ![realDirName isEqual:folder])
191 if (![_caseWarnings containsObject:lcDirName])
193 [_caseWarnings addObject:lcDirName];
194 OOLog(
@"verifyOXP.files.caseMismatch",
@"***** ERROR: case mismatch: directory '%@' should be called '%@'.", realDirName, folder);
198 if (![realFileName isEqual:file])
201 if (![_caseWarnings containsObject:lcName])
203 [_caseWarnings addObject:lcName];
205 expectedPath = [
self displayNameForFile:file andFolder:folder];
207 if (context !=
nil) context = [@" referenced in " stringByAppendingString:context];
210 OOLog(
@"verifyOXP.files.caseMismatch",
@"***** ERROR: case mismatch: request for file '%@'%@ resolved to '%@'.", expectedPath, context, path);
214 return [_basePath stringByAppendingPathComponent:path];
225- (NSData *)dataForFile:(NSString *)file
226 inFolder:(NSString *)folder
227 referencedFrom:(NSString *)context
228 checkBuiltIn:(BOOL)checkBuiltIn
230 NSString *path =
nil;
232 path = [
self pathForFile:file inFolder:folder referencedFrom:context checkBuiltIn:checkBuiltIn];
233 if (path ==
nil)
return nil;
235 return [NSData dataWithContentsOfMappedFile:path];
239- (id)plistNamed:(NSString *)file
240 inFolder:(NSString *)folder
241 referencedFrom:(NSString *)context
242 checkBuiltIn:(BOOL)checkBuiltIn
245 NSString *errorString =
nil;
246 NSPropertyListFormat format;
248 NSArray *errorLines =
nil;
249 NSEnumerator *errLineEnum =
nil;
250 NSString *displayName =
nil,
252 NSAutoreleasePool *pool =
nil;
254 data = [
self dataForFile:file inFolder:folder referencedFrom:context checkBuiltIn:checkBuiltIn];
255 if (data ==
nil)
return nil;
257 pool = [[NSAutoreleasePool alloc] init];
259 plist = [NSPropertyListSerialization propertyListFromData:data
260 mutabilityOption:NSPropertyListImmutable
262 errorDescription:&errorString];
264#if OOLITE_RELEASE_PLIST_ERROR_STRINGS
265 [errorString autorelease];
271 [
self checkPListFormat:format file:file folder:folder];
279 displayName = [
self displayNameForFile:file andFolder:folder];
280 errorKey = [displayName lowercaseString];
281 if (![_badPLists containsObject:errorKey])
283 [_badPLists addObject:errorKey];
284 OOLog(
@"verifyOXP.plist.parseError",
@"Could not interpret property list %@.", displayName);
286 errorLines = [errorString componentsSeparatedByString:@"\n"];
287 for (errLineEnum = [errorLines objectEnumerator]; (errorString = [errLineEnum nextObject]); )
289 while ([errorString hasPrefix:
@"\t"])
291 errorString = [@" " stringByAppendingString:[errorString substringFromIndex:1]];
293 OOLog(
@"verifyOXP.plist.parseError",
@"%@", errorString);
302 return [plist autorelease];
306- (id)displayNameForFile:(NSString *)file andFolder:(NSString *)folder
308 if (file !=
nil && folder !=
nil)
return [folder stringByAppendingPathComponent:file];
313- (NSArray *)filesInFolder:(NSString *)folder
315 if (folder ==
nil)
return nil;
316 return [[_directoryListings objectForKey:[folder lowercaseString]] allValues];
322@implementation OOFileScannerVerifierStage (OOPrivate)
326 NSDirectoryEnumerator *dirEnum =
nil;
327 NSString *name =
nil,
333 NSMutableDictionary *directoryListings =
nil,
334 *directoryCases =
nil,
336 NSDictionary *dirFiles =
nil;
337 NSSet *readMeNames =
nil;
339 _basePath = [[[
self verifier] oxpPath] copy];
341 _junkFileNames = [[
self verifier] configurationSetForKey:@"junkFiles"];
342 _skipDirectoryNames = [[
self verifier] configurationSetForKey:@"skipDirectories"];
344 directoryCases = [NSMutableDictionary dictionary];
345 directoryListings = [NSMutableDictionary dictionary];
346 rootFiles = [NSMutableDictionary dictionary];
347 readMeNames = [
self constructReadMeNames];
349 dirEnum = [[NSFileManager defaultManager] enumeratorAtPath:_basePath];
350 while ((name = [dirEnum nextObject]))
352 path = [_basePath stringByAppendingPathComponent:name];
353 type = [[dirEnum fileAttributes] fileType];
354 lcName = [name lowercaseString];
356 if ([type isEqualToString:NSFileTypeDirectory])
358 [dirEnum skipDescendents];
360 if ([_skipDirectoryNames containsObject:name])
363 OOLog(
@"verifyOXP.verbose.listFiles",
@"- Skipping %@/", name);
365 else if (!
CheckNameConflict(lcName, directoryCases, rootFiles, &existing, &existingType))
367 OOLog(
@"verifyOXP.verbose.listFiles",
@"- %@/", name);
369 dirFiles = [
self scanDirectory:path];
370 [directoryListings setObject:dirFiles forKey:lcName];
371 [directoryCases setObject:name forKey:lcName];
376 OOLog(
@"verifyOXP.scanFiles.overloadedName",
@"***** ERROR: %@ '%@' conflicts with %@ named '%@', ignoring. (OXPs must work on case-insensitive file systems!)",
@"directory", name, existingType, existing);
379 else if ([type isEqualToString:NSFileTypeRegular])
381 if ([_junkFileNames containsObject:name])
383 OOLog(
@"verifyOXP.scanFiles.skipJunk",
@"NOTE: skipping junk file %@.", name);
385 else if ([readMeNames containsObject:lcName])
387 OOLog(
@"verifyOXP.scanFiles.readMe",
@"----- WARNING: apparent Read Me file (\"%@\
") inside OXP. This is the wrong place for a Read Me file, because it will not be read.", name);
389 else if (!
CheckNameConflict(lcName, directoryCases, rootFiles, &existing, &existingType))
391 OOLog(
@"verifyOXP.verbose.listFiles",
@"- %@", name);
392 [rootFiles setObject:name forKey:lcName];
396 OOLog(
@"verifyOXP.scanFiles.overloadedName",
@"***** ERROR: %@ '%@' conflicts with %@ named '%@', ignoring. (OXPs must work on case-insensitive file systems!)",
@"file", name, existingType, existing);
399 else if ([type isEqualToString:NSFileTypeSymbolicLink])
401 OOLog(
@"verifyOXP.scanFiles.symLink",
@"----- WARNING: \"%@\
" is a symbolic link, ignoring.", name);
405 OOLog(
@"verifyOXP.scanFiles.nonStandardFile",
@"----- WARNING: \"%@\
" is a non-standard file (%@), ignoring.", name, type);
409 _junkFileNames =
nil;
410 _skipDirectoryNames =
nil;
412 [directoryListings setObject:rootFiles forKey:@""];
413 _directoryListings = [directoryListings copy];
414 _directoryCases = [directoryCases copy];
420 NSArray *knownNames =
nil;
421 NSEnumerator *nameEnum =
nil;
422 NSString *name =
nil;
423 NSString *lcName =
nil;
424 NSString *actual =
nil;
426 knownNames = [[
self verifier] configurationArrayForKey:@"knownRootDirectories"];
427 for (nameEnum = [knownNames objectEnumerator]; (name = [nameEnum nextObject]); )
429 if (![name isKindOfClass:[NSString
class]])
continue;
431 lcName = [name lowercaseString];
432 actual = [_directoryCases objectForKey:lcName];
433 if (actual ==
nil)
continue;
435 if (![actual isEqualToString:name])
437 OOLog(
@"verifyOXP.files.caseMismatch",
@"***** ERROR: case mismatch: directory '%@' should be called '%@'.", actual, name);
439 [_caseWarnings addObject:lcName];
446 NSArray *knownNames =
nil;
447 NSEnumerator *nameEnum =
nil;
448 NSString *name =
nil,
453 knownNames = [[
self verifier] configurationArrayForKey:@"knownConfigFiles"];
454 for (nameEnum = [knownNames objectEnumerator]; (name = [nameEnum nextObject]); )
456 if (![name isKindOfClass:[NSString
class]])
continue;
462 lcName = [name lowercaseString];
463 realFileName = [[_directoryListings oo_dictionaryForKey:@"config"] objectForKey:lcName];
464 inConfigDir = realFileName !=
nil;
465 if (!inConfigDir) realFileName = [[_directoryListings oo_dictionaryForKey:@""] objectForKey:lcName];
466 if (realFileName ==
nil)
continue;
468 if (![realFileName isEqualToString:name])
470 if (inConfigDir) realFileName = [@"Config" stringByAppendingPathComponent:realFileName];
471 OOLog(
@"verifyOXP.files.caseMismatch",
@"***** ERROR: case mismatch: configuration file '%@' should be called '%@'.", realFileName, name);
479 NSDictionary *directories =
nil;
480 NSEnumerator *directoryEnum =
nil;
481 NSString *directory =
nil,
483 NSArray *fileList =
nil;
484 NSEnumerator *nameEnum =
nil;
485 NSString *name =
nil,
490 directories = [[
self verifier] configurationDictionaryForKey:@"knownFiles"];
491 for (directoryEnum = [directories keyEnumerator]; (directory = [directoryEnum nextObject]); )
493 fileList = [directories objectForKey:directory];
494 lcDirectory = [directory lowercaseString];
495 for (nameEnum = [fileList objectEnumerator]; (name = [nameEnum nextObject]); )
497 if (![name isKindOfClass:[NSString
class]])
continue;
503 lcName = [name lowercaseString];
504 realFileName = [[_directoryListings oo_dictionaryForKey:lcDirectory] objectForKey:lcName];
505 inDirectory = (realFileName !=
nil);
509 realFileName = [[_directoryListings oo_dictionaryForKey:@""] objectForKey:lcName];
511 if (realFileName ==
nil)
continue;
513 if (![realFileName isEqualToString:name])
515 if (inDirectory) realFileName = [directory stringByAppendingPathComponent:realFileName];
516 OOLog(
@"verifyOXP.files.caseMismatch",
@"***** ERROR: case mismatch: file '%@' should be called '%@'.", realFileName, name);
523- (NSDictionary *)lowercaseMap:(NSArray *)array
526 NSString *canonical =
nil,
528 NSMutableDictionary *result =
nil;
530 count = [array count];
531 if (
count == 0)
return [NSDictionary dictionary];
532 result = [NSMutableDictionary dictionaryWithCapacity:count];
534 for (i = 0; i !=
count; ++i)
536 canonical = [array oo_stringAtIndex:i];
537 if (canonical !=
nil)
539 lowercase = [canonical lowercaseString];
540 [result setObject:canonical forKey:lowercase];
548- (NSDictionary *)scanDirectory:(NSString *)path
550 NSDirectoryEnumerator *dirEnum =
nil;
551 NSMutableDictionary *result =
nil;
552 NSString *name =
nil,
559 result = [NSMutableDictionary dictionary];
560 dirName = [path lastPathComponent];
562 dirEnum = [[NSFileManager defaultManager] enumeratorAtPath:path];
563 while ((name = [dirEnum nextObject]))
565 type = [[dirEnum fileAttributes] fileType];
566 relativeName = [dirName stringByAppendingPathComponent:name];
568 if ([_junkFileNames containsObject:name])
570 OOLog(
@"verifyOXP.scanFiles.skipJunk",
@"NOTE: skipping junk file %@/%@.", dirName, name);
572 else if ([type isEqualToString:NSFileTypeRegular])
574 lcName = [name lowercaseString];
575 existing = [result objectForKey:lcName];
579 OOLog(
@"verifyOXP.verbose.listFiles",
@"- %@", name);
580 [result setObject:name forKey:lcName];
584 OOLog(
@"verifyOXP.scanFiles.overloadedName",
@"***** ERROR: %@ '%@' conflicts with %@ named '%@', ignoring. (OXPs must work on case-insensitive file systems!)",
@"file", relativeName,
@"file", [dirName stringByAppendingPathComponent:existing]);
589 if ([type isEqualToString:NSFileTypeDirectory])
591 [dirEnum skipDescendents];
592 if (![_skipDirectoryNames containsObject:name])
594 OOLog(
@"verifyOXP.scanFiles.directory",
@"----- WARNING: \"%@\
" is a nested directory, ignoring.", relativeName);
598 OOLog(
@"verifyOXP.verbose.listFiles",
@"- Skipping %@/%@/", dirName, name);
601 else if ([type isEqualToString:NSFileTypeSymbolicLink])
603 OOLog(
@"verifyOXP.scanFiles.symLink",
@"----- WARNING: \"%@\
" is a symbolic link, ignoring.", relativeName);
607 OOLog(
@"verifyOXP.scanFiles.nonStandardFile",
@"----- WARNING: \"%@\
" is a non-standard file (%@), ignoring.", relativeName, type);
616- (void)checkPListFormat:(NSPropertyListFormat)format file:(NSString *)file folder:(NSString *)folder
618 NSString *weirdnessKey =
nil;
619 NSString *formatDesc =
nil;
620 NSString *displayPath =
nil;
622 if (format != NSPropertyListOpenStepFormat && format != NSPropertyListXMLFormat_v1_0)
624 displayPath = [
self displayNameForFile:file andFolder:folder];
625 weirdnessKey = [displayPath lowercaseString];
627 if (![_badPLists containsObject:weirdnessKey])
630 [_badPLists addObject:weirdnessKey];
634 case NSPropertyListBinaryFormat_v1_0:
635 formatDesc =
@"Apple binary format";
639 case NSPropertyListGNUstepFormat:
640 formatDesc =
@"GNUstep text format";
643 case NSPropertyListGNUstepBinaryFormat:
644 formatDesc =
@"GNUstep binary format";
649 formatDesc = [NSString stringWithFormat:@"unknown format (%i)", (int)format];
652 OOLog(
@"verifyOXP.plist.weirdFormat",
@"----- WARNING: Property list %@ is in %@; OpenStep text format and XML format are the recommended formats for Oolite.", displayPath, formatDesc);
660 NSDictionary *dict =
nil;
661 NSArray *stems =
nil,
663 NSMutableSet *result =
nil;
664 NSUInteger i, j, stemCount, extCount;
665 NSString *stem =
nil,
668 dict = [[
self verifier] configurationDictionaryForKey:@"readMeNames"];
669 stems = [dict oo_arrayForKey:@"stems"];
670 extensions = [dict oo_arrayForKey:@"extensions"];
671 stemCount = [stems count];
672 extCount = [extensions count];
673 if (stemCount * extCount == 0)
return nil;
676 result = [NSMutableSet setWithCapacity:stemCount * extCount];
677 for (i = 0; i != stemCount; ++i)
679 stem = [[stems oo_stringAtIndex:i] lowercaseString];
682 for (j = 0; j != extCount; ++j)
684 extension = [[extensions oo_stringAtIndex:j] lowercaseString];
685 if (extension !=
nil)
687 [result addObject:[stem stringByAppendingString:extension]];
707- (NSSet *)dependencies
709 return [NSSet setWithObject:kFileScannerStageName];
715 OOLog(
@"verifyOXP.unusedFiles.unimplemented",
@"%@",
@"TODO: implement unused files check.");
719+ (NSString *)nameForReverseDependencyForVerifier:(
OOOXPVerifier *)verifier
739 return [
self stageWithName:kFileScannerStageName];
747- (NSSet *)dependencies
761static BOOL
CheckNameConflict(NSString *lcName, NSDictionary *directoryCases, NSDictionary *rootFiles, NSString **outExisting, NSString **outExistingType)
763 NSString *existing =
nil;
765 existing = [directoryCases objectForKey:lcName];
768 if (outExisting != NULL) *outExisting = existing;
769 if (outExistingType != NULL) *outExistingType =
@"directory";
773 existing = [rootFiles objectForKey:lcName];
776 if (outExisting != NULL) *outExisting = existing;
777 if (outExistingType != NULL) *outExistingType =
@"file";
static NSString *const kUnusedListerStageName
static BOOL CheckNameConflict(NSString *lcName, NSDictionary *directoryCases, NSDictionary *rootFiles, NSString **outExisting, NSString **outExistingType)
static NSString *const kFileScannerStageName
#define OOLogOutdentIf(class)
#define OOLog(class, format,...)
#define OOLogIndentIf(class)
NSSet * constructReadMeNames()
NSString * nameForDependencyForVerifier:(OOOXPVerifier *verifier)
NSString * nameForReverseDependencyForVerifier:(OOOXPVerifier *verifier)
id stageWithName:(NSString *name)
void registerStage:(OOOXPVerifierStage *stage)
NSString * pathForFileNamed:inFolder:(NSString *fileName,[inFolder] NSString *folderName)