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 NSString *displayName =
nil,
251 NSAutoreleasePool *pool =
nil;
253 data = [
self dataForFile:file inFolder:folder referencedFrom:context checkBuiltIn:checkBuiltIn];
254 if (data ==
nil)
return nil;
256 pool = [[NSAutoreleasePool alloc] init];
258 plist = [NSPropertyListSerialization propertyListFromData:data
259 mutabilityOption:NSPropertyListImmutable
261 errorDescription:&errorString];
263#if OOLITE_RELEASE_PLIST_ERROR_STRINGS
264 [errorString autorelease];
270 [
self checkPListFormat:format file:file folder:folder];
278 displayName = [
self displayNameForFile:file andFolder:folder];
279 errorKey = [displayName lowercaseString];
280 if (![_badPLists containsObject:errorKey])
282 [_badPLists addObject:errorKey];
283 OOLog(
@"verifyOXP.plist.parseError",
@"Could not interpret property list %@.", displayName);
285 errorLines = [errorString componentsSeparatedByString:@"\n"];
286 foreach (errorString, errorLines)
288 while ([errorString hasPrefix:
@"\t"])
290 errorString = [@" " stringByAppendingString:[errorString substringFromIndex:1]];
292 OOLog(
@"verifyOXP.plist.parseError",
@"%@", errorString);
301 return [plist autorelease];
305- (id)displayNameForFile:(NSString *)file andFolder:(NSString *)folder
307 if (file !=
nil && folder !=
nil)
return [folder stringByAppendingPathComponent:file];
312- (NSArray *)filesInFolder:(NSString *)folder
314 if (folder ==
nil)
return nil;
315 return [[_directoryListings objectForKey:[folder lowercaseString]] allValues];
321@implementation OOFileScannerVerifierStage (OOPrivate)
325 NSDirectoryEnumerator *dirEnum =
nil;
326 NSString *name =
nil,
332 NSMutableDictionary *directoryListings =
nil,
333 *directoryCases =
nil,
335 NSDictionary *dirFiles =
nil;
336 NSSet *readMeNames =
nil;
338 _basePath = [[[
self verifier] oxpPath] copy];
340 _junkFileNames = [[
self verifier] configurationSetForKey:@"junkFiles"];
341 _skipDirectoryNames = [[
self verifier] configurationSetForKey:@"skipDirectories"];
343 directoryCases = [NSMutableDictionary dictionary];
344 directoryListings = [NSMutableDictionary dictionary];
345 rootFiles = [NSMutableDictionary dictionary];
346 readMeNames = [
self constructReadMeNames];
348 dirEnum = [[NSFileManager defaultManager] enumeratorAtPath:_basePath];
349 while ((name = [dirEnum nextObject]))
351 path = [_basePath stringByAppendingPathComponent:name];
352 type = [[dirEnum fileAttributes] fileType];
353 lcName = [name lowercaseString];
355 if ([type isEqualToString:NSFileTypeDirectory])
357 [dirEnum skipDescendents];
359 if ([_skipDirectoryNames containsObject:name])
362 OOLog(
@"verifyOXP.verbose.listFiles",
@"- Skipping %@/", name);
364 else if (!
CheckNameConflict(lcName, directoryCases, rootFiles, &existing, &existingType))
366 OOLog(
@"verifyOXP.verbose.listFiles",
@"- %@/", name);
368 dirFiles = [
self scanDirectory:path];
369 [directoryListings setObject:dirFiles forKey:lcName];
370 [directoryCases setObject:name forKey:lcName];
375 OOLog(
@"verifyOXP.scanFiles.overloadedName",
@"***** ERROR: %@ '%@' conflicts with %@ named '%@', ignoring. (OXPs must work on case-insensitive file systems!)",
@"directory", name, existingType, existing);
378 else if ([type isEqualToString:NSFileTypeRegular])
380 if ([_junkFileNames containsObject:name])
382 OOLog(
@"verifyOXP.scanFiles.skipJunk",
@"NOTE: skipping junk file %@.", name);
384 else if ([readMeNames containsObject:lcName])
386 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);
388 else if (!
CheckNameConflict(lcName, directoryCases, rootFiles, &existing, &existingType))
390 OOLog(
@"verifyOXP.verbose.listFiles",
@"- %@", name);
391 [rootFiles setObject:name forKey:lcName];
395 OOLog(
@"verifyOXP.scanFiles.overloadedName",
@"***** ERROR: %@ '%@' conflicts with %@ named '%@', ignoring. (OXPs must work on case-insensitive file systems!)",
@"file", name, existingType, existing);
398 else if ([type isEqualToString:NSFileTypeSymbolicLink])
400 OOLog(
@"verifyOXP.scanFiles.symLink",
@"----- WARNING: \"%@\
" is a symbolic link, ignoring.", name);
404 OOLog(
@"verifyOXP.scanFiles.nonStandardFile",
@"----- WARNING: \"%@\
" is a non-standard file (%@), ignoring.", name, type);
408 _junkFileNames =
nil;
409 _skipDirectoryNames =
nil;
411 [directoryListings setObject:rootFiles forKey:@""];
412 _directoryListings = [directoryListings copy];
413 _directoryCases = [directoryCases copy];
419 NSArray *knownNames =
nil;
420 NSString *name =
nil;
421 NSString *lcName =
nil;
422 NSString *actual =
nil;
424 knownNames = [[
self verifier] configurationArrayForKey:@"knownRootDirectories"];
425 foreach (name, knownNames)
427 if (![name isKindOfClass:[NSString class]]) continue;
429 lcName = [name lowercaseString];
430 actual = [_directoryCases objectForKey:lcName];
431 if (actual ==
nil)
continue;
433 if (![actual isEqualToString:name])
435 OOLog(
@"verifyOXP.files.caseMismatch",
@"***** ERROR: case mismatch: directory '%@' should be called '%@'.", actual, name);
437 [_caseWarnings addObject:lcName];
444 NSArray *knownNames =
nil;
445 NSString *name =
nil,
450 knownNames = [[
self verifier] configurationArrayForKey:@"knownConfigFiles"];
451 foreach (name, knownNames)
453 if (![name isKindOfClass:[NSString class]]) continue;
459 lcName = [name lowercaseString];
460 realFileName = [[_directoryListings oo_dictionaryForKey:@"config"] objectForKey:lcName];
461 inConfigDir = realFileName !=
nil;
462 if (!inConfigDir) realFileName = [[_directoryListings oo_dictionaryForKey:@""] objectForKey:lcName];
463 if (realFileName ==
nil)
continue;
465 if (![realFileName isEqualToString:name])
467 if (inConfigDir) realFileName = [@"Config" stringByAppendingPathComponent:realFileName];
468 OOLog(
@"verifyOXP.files.caseMismatch",
@"***** ERROR: case mismatch: configuration file '%@' should be called '%@'.", realFileName, name);
476 NSDictionary *directories =
nil;
477 NSString *directory =
nil,
479 NSArray *fileList =
nil;
480 NSString *name =
nil,
485 directories = [[
self verifier] configurationDictionaryForKey:@"knownFiles"];
488 fileList = [directories objectForKey:directory];
489 lcDirectory = [directory lowercaseString];
490 foreach (name, fileList)
492 if (![name isKindOfClass:[NSString class]]) continue;
498 lcName = [name lowercaseString];
499 realFileName = [[_directoryListings oo_dictionaryForKey:lcDirectory] objectForKey:lcName];
500 inDirectory = (realFileName !=
nil);
504 realFileName = [[_directoryListings oo_dictionaryForKey:@""] objectForKey:lcName];
506 if (realFileName ==
nil)
continue;
508 if (![realFileName isEqualToString:name])
510 if (inDirectory) realFileName = [directory stringByAppendingPathComponent:realFileName];
511 OOLog(
@"verifyOXP.files.caseMismatch",
@"***** ERROR: case mismatch: file '%@' should be called '%@'.", realFileName, name);
518- (NSDictionary *)lowercaseMap:(NSArray *)array
521 NSString *canonical =
nil,
523 NSMutableDictionary *result =
nil;
525 count = [array count];
526 if (
count == 0)
return [NSDictionary dictionary];
527 result = [NSMutableDictionary dictionaryWithCapacity:count];
529 for (i = 0; i !=
count; ++i)
531 canonical = [array oo_stringAtIndex:i];
532 if (canonical !=
nil)
534 lowercase = [canonical lowercaseString];
535 [result setObject:canonical forKey:lowercase];
543- (NSDictionary *)scanDirectory:(NSString *)path
545 NSDirectoryEnumerator *dirEnum =
nil;
546 NSMutableDictionary *result =
nil;
547 NSString *name =
nil,
554 result = [NSMutableDictionary dictionary];
555 dirName = [path lastPathComponent];
557 dirEnum = [[NSFileManager defaultManager] enumeratorAtPath:path];
558 while ((name = [dirEnum nextObject]))
560 type = [[dirEnum fileAttributes] fileType];
561 relativeName = [dirName stringByAppendingPathComponent:name];
563 if ([_junkFileNames containsObject:name])
565 OOLog(
@"verifyOXP.scanFiles.skipJunk",
@"NOTE: skipping junk file %@/%@.", dirName, name);
567 else if ([type isEqualToString:NSFileTypeRegular])
569 lcName = [name lowercaseString];
570 existing = [result objectForKey:lcName];
574 OOLog(
@"verifyOXP.verbose.listFiles",
@"- %@", name);
575 [result setObject:name forKey:lcName];
579 OOLog(
@"verifyOXP.scanFiles.overloadedName",
@"***** ERROR: %@ '%@' conflicts with %@ named '%@', ignoring. (OXPs must work on case-insensitive file systems!)",
@"file", relativeName,
@"file", [dirName stringByAppendingPathComponent:existing]);
584 if ([type isEqualToString:NSFileTypeDirectory])
586 [dirEnum skipDescendents];
587 if (![_skipDirectoryNames containsObject:name])
589 OOLog(
@"verifyOXP.scanFiles.directory",
@"----- WARNING: \"%@\
" is a nested directory, ignoring.", relativeName);
593 OOLog(
@"verifyOXP.verbose.listFiles",
@"- Skipping %@/%@/", dirName, name);
596 else if ([type isEqualToString:NSFileTypeSymbolicLink])
598 OOLog(
@"verifyOXP.scanFiles.symLink",
@"----- WARNING: \"%@\
" is a symbolic link, ignoring.", relativeName);
602 OOLog(
@"verifyOXP.scanFiles.nonStandardFile",
@"----- WARNING: \"%@\
" is a non-standard file (%@), ignoring.", relativeName, type);
611- (void)checkPListFormat:(NSPropertyListFormat)format file:(NSString *)file folder:(NSString *)folder
613 NSString *weirdnessKey =
nil;
614 NSString *formatDesc =
nil;
615 NSString *displayPath =
nil;
617 if (format != NSPropertyListOpenStepFormat && format != NSPropertyListXMLFormat_v1_0)
619 displayPath = [
self displayNameForFile:file andFolder:folder];
620 weirdnessKey = [displayPath lowercaseString];
622 if (![_badPLists containsObject:weirdnessKey])
625 [_badPLists addObject:weirdnessKey];
629 case NSPropertyListBinaryFormat_v1_0:
630 formatDesc =
@"Apple binary format";
634 case NSPropertyListGNUstepFormat:
635 formatDesc =
@"GNUstep text format";
638 case NSPropertyListGNUstepBinaryFormat:
639 formatDesc =
@"GNUstep binary format";
644 formatDesc = [NSString stringWithFormat:@"unknown format (%i)", (int)format];
647 OOLog(
@"verifyOXP.plist.weirdFormat",
@"----- WARNING: Property list %@ is in %@; OpenStep text format and XML format are the recommended formats for Oolite.", displayPath, formatDesc);
655 NSDictionary *dict =
nil;
656 NSArray *stems =
nil,
658 NSMutableSet *result =
nil;
659 NSUInteger i, j, stemCount, extCount;
660 NSString *stem =
nil,
663 dict = [[
self verifier] configurationDictionaryForKey:@"readMeNames"];
664 stems = [dict oo_arrayForKey:@"stems"];
665 extensions = [dict oo_arrayForKey:@"extensions"];
666 stemCount = [stems count];
667 extCount = [extensions count];
668 if (stemCount * extCount == 0)
return nil;
671 result = [NSMutableSet setWithCapacity:stemCount * extCount];
672 for (i = 0; i != stemCount; ++i)
674 stem = [[stems oo_stringAtIndex:i] lowercaseString];
677 for (j = 0; j != extCount; ++j)
679 extension = [[extensions oo_stringAtIndex:j] lowercaseString];
680 if (extension !=
nil)
682 [result addObject:[stem stringByAppendingString:extension]];
702- (NSSet *)dependencies
704 return [NSSet setWithObject:kFileScannerStageName];
710 OOLog(
@"verifyOXP.unusedFiles.unimplemented",
@"%@",
@"TODO: implement unused files check.");
714+ (NSString *)nameForReverseDependencyForVerifier:(
OOOXPVerifier *)verifier
734 return [
self stageWithName:kFileScannerStageName];
742- (NSSet *)dependencies
756static BOOL
CheckNameConflict(NSString *lcName, NSDictionary *directoryCases, NSDictionary *rootFiles, NSString **outExisting, NSString **outExistingType)
758 NSString *existing =
nil;
760 existing = [directoryCases objectForKey:lcName];
763 if (outExisting != NULL) *outExisting = existing;
764 if (outExistingType != NULL) *outExistingType =
@"directory";
768 existing = [rootFiles objectForKey:lcName];
771 if (outExisting != NULL) *outExisting = existing;
772 if (outExistingType != NULL) *outExistingType =
@"file";
#define foreachkey(VAR, DICT)
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)