OXP verifier can now identify bad case in standard folder names and config file names. Infrastructure is in place for general case mismatch detection. Fixed Windows build warnings reported by Sotho Tal Ker in forum.

git-svn-id: http://svn.berlios.de/svnroot/repos/oolite-linux/trunk@1094 127b21dd-08f5-0310-b4b7-95ae10353056
This commit is contained in:
Jens Ayton 2007-07-18 23:53:51 +00:00
parent 9aa1e3947e
commit 7780ef63bb
11 changed files with 399 additions and 23 deletions

View File

@ -1270,7 +1270,7 @@
1AB01BBA0BB16A8A00F1B949 /* OOFastArithmetic.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OOFastArithmetic.m; sourceTree = "<group>"; };
1AB2AAF80C4CE0CC0008CF4E /* OOOXPVerifier.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = OOOXPVerifier.h; sourceTree = "<group>"; };
1AB2AAF90C4CE0CC0008CF4E /* OOOXPVerifier.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = OOOXPVerifier.m; sourceTree = "<group>"; };
1AB2AB120C4CE4070008CF4E /* verifyOXP.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist; path = verifyOXP.plist; sourceTree = "<group>"; };
1AB2AB120C4CE4070008CF4E /* verifyOXP.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist; name = verifyOXP.plist; path = ../../../Resources/Config/verifyOXP.plist; sourceTree = "<group>"; };
1AC0E9460B974DC200C46994 /* GPL.TXT */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = GPL.TXT; sourceTree = "<group>"; };
1AC0E9470B974DC200C46994 /* FAQ.TXT */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = FAQ.TXT; sourceTree = "<group>"; };
1AC0E9480B974DC200C46994 /* ReadMe.rtfd */ = {isa = PBXFileReference; lastKnownFileType = wrapper.rtfd; path = ReadMe.rtfd; sourceTree = "<group>"; };
@ -1539,7 +1539,6 @@
1A2316EC0B9CFAD700EF0852 /* shipyard.plist */,
1A2316ED0B9CFAD700EF0852 /* speech_pronunciation_guide.plist */,
1A3591820C1C382700E52220 /* startextures.plist */,
1AB2AB120C4CE4070008CF4E /* verifyOXP.plist */,
1A3491290BC25EAA00802DA7 /* world-scripts.plist */,
);
path = Config;
@ -2189,6 +2188,7 @@
1AB2AAE70C4CDF890008CF4E /* OXP Verification */ = {
isa = PBXGroup;
children = (
1AB2AB120C4CE4070008CF4E /* verifyOXP.plist */,
1AB2AAF80C4CE0CC0008CF4E /* OOOXPVerifier.h */,
1AB2AAF90C4CE0CC0008CF4E /* OOOXPVerifier.m */,
1A27DB390C4E349F00CB4CE8 /* OOOXPVerifierStage.h */,

View File

@ -294,6 +294,9 @@
universe.populate.witchspace = inherit;
verifyOXP.verbose = no;
/*** Mac OS X/Cocoa-specific ***/
growl.error = $error;
growl.debug = no;

View File

@ -448,5 +448,7 @@
<string>no</string>
<key>universe.populate.witchspace</key>
<string>inherit</string>
<key>verifyOXP.verbose</key>
<string>no</string>
</dict>
</plist>

View File

@ -37,19 +37,23 @@
knownConfigFiles =
(
// Files that are implicitly considered to be "used". May occur in root or in Config/.
"autoAImap.plist",
"characters.plist",
"commodities.plist",
"customsounds.plist",
"demoships.plist",
"descriptions.plist",
"equipment.plist",
"hud.plist",
"illegal_goods.plist",
"keyconfig.plist",
"material-defaults.plist",
"missiontext.plist",
"nebulatextures.plist",
"planetinfo.plist",
"requires.plist",
"script.plist",
"script.js",
"shipdata.plist",
"shipyard.plist",
"speech_pronounciation_guide.plist",

View File

@ -1050,7 +1050,7 @@ static NSTimeInterval time_last_frame;
OOLogSetDisplayMessagesInClass(@"$shaderDebugOn", NO);
#ifdef ALLOW_PROCEDURAL_PLANETS
[UNIVERSE setDoProcedurallyTexturedPlanets: NO];
#endif ALLOW_PROCEDURAL_PLANETS
#endif // ALLOW_PROCEDURAL_PLANETS
}
}
//

View File

@ -57,7 +57,7 @@ SOFTWARE.
#import "OOOpenGLExtensionManager.h"
#import "OOShaderMaterial.h"
#import "OOSingleTextureMaterial.h"
#import "OOCollectionExtractors.h";
#import "OOCollectionExtractors.h"
static OOMaterial *sActiveMaterial = nil;

View File

@ -102,7 +102,7 @@ static unsigned IntegerFromString(const GLubyte **ioString);
#ifndef NO_SHADERS
- (void)checkShadersSupported;
#endif NO_SHADERS
#endif // NO_SHADERS
@end

View File

@ -3,7 +3,8 @@
OOFileScannerVerifierStage.h
OOOXPVerifierStage which keeps track of which files are used and ensures file
name capitalization is consistent.
name capitalization is consistent. It also provides the file lookup service
for other stages.
Oolite
@ -58,10 +59,28 @@ extern NSString * const kOOFileScannerVerifierStageName;
@interface OOFileScannerVerifierStage: OOOXPVerifierStage
{
NSMutableSet *_foundFiles;
NSString *_basePath;
NSMutableSet *_usedFiles;
NSMutableSet *_caseWarnings;
NSDictionary *_directoryListings;
NSDictionary *_directoryCases;
}
/* This method does the following:
A. Checks whether a file exists.
B. Checks whether case matches, and logs a warning otherwise.
C. Maintains list of files which are referred to.
D. Optionally falls back on Oolite's built-in files.
For example, to test whether a texture referenced in a shipdata.plist entry
exists, one would use:
[fileScanner fileExists:textureName inFolder:@"Textures" referencedFrom:@"shipdata.plist" checkBuiltIn:YES];
*/
- (BOOL)fileExists:(NSString *)file inFolder:(NSString *)folder referencedFrom:(NSString *)context checkBuiltIn:(BOOL)checkBuiltIn;
// This method performs all the checks the previous one does, but also returns a file path.
- (NSString *)pathForFile:(NSString *)file inFolder:(NSString *)folder referencedFrom:(NSString *)context checkBuiltIn:(BOOL)checkBuiltIn;
@end
#endif

View File

@ -46,16 +46,56 @@ SOFTWARE.
*/
/* Design notes:
In order to be able to look files up case-insenstively, but warn about
case mismatches, the OOFileScannerVerifierStage builds its own
representation of the file hierarchy. Dictionaries are used heavily: the
_directoryListings is keyed by folder names mapped to lower case, and its
entries map lowercase file names to actual case, that is, the case found
in the file system. The companion dictionary _directoryCases maps
lowercase directory names to actual case.
The class design is based on the knowledge that Oolite uses a two-level
namespace for files. Each file type has an appropriate folder, and files
may either be in the appropriate folder or "bare". For instance, a texture
file in an OXP may be either in the Textures subdirectory or in the root
directory of the OXP. The root directory's contents are listed in
_directoryListings with the empty string as key. This architecture means
the OOFileScannerVerifierStage doesn't need to take full file system
hierarchy into account.
*/
#import "OOFileScannerVerifierStage.h"
#if OO_OXP_VERIFIER_ENABLED
#import "OOCollectionExtractors.h"
#import "ResourceManager.h"
NSString * const kOOFileScannerVerifierStageName = @"Scanning files";
static BOOL CheckNameConflict(NSString *lcName, NSDictionary *directoryCases, NSDictionary *rootFiles, NSString **outExisting, NSString **outExistingType);
@interface OOFileScannerVerifierStage (OOPrivate)
- (void)setUp;
- (void)scanForFiles;
- (void)checkRootFolders;
- (void)checkConfigFiles;
/* Given an array of strings, return a dictionary mapping lowercase strings
to the canonicial case given in the array. For instance, given
(Foo, BAR)
it will return
{ foo = Foo; bar = BAR }
*/
- (NSDictionary *)lowercaseMap:(NSArray *)array;
- (NSDictionary *)scanDirectory:(NSString *)path;
@end
@ -64,8 +104,11 @@ NSString * const kOOFileScannerVerifierStageName = @"Scanning files";
- (void)dealloc
{
[_foundFiles release];
[_basePath release];
[_usedFiles release];
[_caseWarnings release];
[_directoryListings release];
[_directoryCases release];
[super dealloc];
}
@ -79,7 +122,19 @@ NSString * const kOOFileScannerVerifierStageName = @"Scanning files";
- (void)run
{
[self setUp];
NSAutoreleasePool *pool = nil;
_usedFiles = [[NSMutableSet alloc] init];
_caseWarnings = [[NSMutableSet alloc] init];
pool = [[NSAutoreleasePool alloc] init];
[self scanForFiles];
[pool release];
pool = [[NSAutoreleasePool alloc] init];
[self checkRootFolders];
[self checkConfigFiles];
[pool release];
}
@ -94,29 +149,321 @@ NSString * const kOOFileScannerVerifierStageName = @"Scanning files";
}
- (BOOL)fileExists:(NSString *)file inFolder:(NSString *)folder referencedFrom:(NSString *)context checkBuiltIn:(BOOL)checkBuiltIn
{
return [self pathForFile:file inFolder:folder referencedFrom:context checkBuiltIn:checkBuiltIn] != nil;
}
- (NSString *)pathForFile:(NSString *)file inFolder:(NSString *)folder referencedFrom:(NSString *)context checkBuiltIn:(BOOL)checkBuiltIn
{
NSString *lcName = nil,
*lcDirName = nil,
*realDirName = nil,
*realFileName = nil,
*path = nil,
*expectedPath = nil;
if (file == nil) return nil;
lcName = [file lowercaseString];
if (folder != nil)
{
lcDirName = [folder lowercaseString];
realFileName = [[_directoryListings objectForKey:lcDirName] objectForKey:lcName];
if (realFileName != nil)
{
realDirName = [_directoryCases objectForKey:lcDirName];
path = [realDirName stringByAppendingPathComponent:realFileName];
}
}
if (path == nil)
{
realFileName = [[_directoryListings objectForKey:@""] objectForKey:lcName];
if (realFileName != nil)
{
path = realFileName;
}
}
if (path != nil)
{
[_usedFiles addObject:path];
if (realDirName != nil && ![realDirName isEqual:folder])
{
// Case mismatch for folder name
if ([_caseWarnings member:lcDirName] == nil)
{
[_caseWarnings addObject:lcDirName];
OOLog(@"verifyOXP.files.caseMismatch", @"ERROR: Case mismatch: directory \"%@\" should be called \"%@\".", realDirName, folder);
}
}
if (![realFileName isEqual:file])
{
// Case mismatch for file name
if ([_caseWarnings member:lcName] == nil)
{
[_caseWarnings addObject:lcName];
if (folder != nil) expectedPath = [folder stringByAppendingPathComponent:file];
else expectedPath = file;
if (context != nil) context = [@" referenced in " stringByAppendingString:context];
else context = @"";
OOLog(@"verifyOXP.files.caseMismatch", @"ERROR: Case mismatch: request for file \"%@\"%@ resolved to \"%@\".", expectedPath, context, path);
}
}
return [_basePath stringByAppendingPathComponent:path];
}
// If we get here, the file wasn't found in the OXP.
if (checkBuiltIn) return [ResourceManager pathForFileNamed:file inFolder:folder];
return nil;
}
@end
@implementation OOFileScannerVerifierStage (OOPrivate)
- (void)setUp
- (void)scanForFiles
{
NSArray *implicitlyUsedFiles = nil;
NSEnumerator *impUsedEnum = nil;
NSString *impUsed = nil;
NSDirectoryEnumerator *dirEnum = nil;
NSString *name = nil, *path = nil;
NSMutableDictionary *directoryListings = nil;
NSMutableDictionary *directoryCases = nil;
NSMutableDictionary *rootFiles = nil;
NSDictionary *dirFiles = nil;
NSString *type = nil;
NSString *lcName = nil;
NSString *existing = nil, *existingType = nil;
_foundFiles = [[NSMutableSet alloc] init];
_usedFiles = [[NSMutableSet alloc] init];
_basePath = [[[self verifier] oxpPath] copy];
// Set up "implicitly used" files.
implicitlyUsedFiles = [[self verifier] configurationValueForKey:@"implictlyUsedConfigFiles"];
for (impUsedEnum = [implicitlyUsedFiles objectEnumerator]; (impUsed = [impUsedEnum nextObject]); )
directoryCases = [NSMutableDictionary dictionary];
directoryListings = [NSMutableDictionary dictionary];
rootFiles = [NSMutableDictionary dictionary];
dirEnum = [[NSFileManager defaultManager] enumeratorAtPath:_basePath];
while ((name = [dirEnum nextObject]))
{
[_usedFiles addObject:impUsed];
[_usedFiles addObject:[@"Config" stringByAppendingPathComponent:impUsed]];
path = [_basePath stringByAppendingPathComponent:name];
type = [[dirEnum fileAttributes] fileType];
lcName = [name lowercaseString];
if ([type isEqualToString:NSFileTypeDirectory])
{
dirFiles = [self scanDirectory:path];
if (!CheckNameConflict(lcName, directoryCases, rootFiles, &existing, &existingType))
{
[directoryListings setObject:dirFiles forKey:lcName];
[directoryCases setObject:name forKey:lcName];
[dirEnum skipDescendents];
}
else
{
OOLog(@"verifyOXP.scanFiles.overloadedName", @"ERROR: %@ named \"%@\" conflicts with %@ named \"%@\", ignoring. (OXPs must work on case-insensitive file systems!)", @"directory", name, existingType, existing);
}
}
else if ([type isEqualToString:NSFileTypeRegular])
{
if (!CheckNameConflict(lcName, directoryCases, rootFiles, &existing, &existingType))
{
[rootFiles setObject:name forKey:lcName];
}
else
{
OOLog(@"verifyOXP.scanFiles.overloadedName", @"ERROR: %@ named \"%@\" conflicts with %@ named \"%@\", ignoring. (OXPs must work on case-insensitive file systems!)", @"file", name, existingType, existing);
}
}
else if ([type isEqualToString:NSFileTypeSymbolicLink])
{
OOLog(@"verifyOXP.scanFiles.symLink", @"WARNING: \"%@\" is a symbolic link, ignoring.", name);
}
else
{
OOLog(@"verifyOXP.scanFiles.nonStandardFile", @"WARNING: \"%@\" is a non-standard file (%@), ignoring.", name, type);
}
}
[directoryListings setObject:rootFiles forKey:@""];
_directoryListings = [directoryListings copy];
_directoryCases = [directoryCases copy];
}
- (void)checkRootFolders
{
NSArray *knownNames = nil;
NSEnumerator *nameEnum = nil;
NSString *name = nil;
NSString *lcName = nil;
NSString *actual = nil;
knownNames = [[self verifier] configurationArrayForKey:@"knownRootDirectories"];
for (nameEnum = [knownNames objectEnumerator]; (name = [nameEnum nextObject]); )
{
if (![name isKindOfClass:[NSString class]]) continue;
lcName = [name lowercaseString];
actual = [_directoryCases objectForKey:lcName];
if (actual == nil) continue;
if (![actual isEqualToString:name])
{
OOLog(@"verifyOXP.files.caseMismatch", @"ERROR: Case mismatch: directory \"%@\" should be called \"%@\".", actual, name);
}
[_caseWarnings addObject:lcName];
}
}
- (void)checkConfigFiles
{
NSArray *knownNames = nil;
NSEnumerator *nameEnum = nil;
NSString *name = nil,
*lcName = nil,
*realFileName = nil;
BOOL inConfigDir;
knownNames = [[self verifier] configurationArrayForKey:@"knownConfigFiles"];
for (nameEnum = [knownNames objectEnumerator]; (name = [nameEnum nextObject]); )
{
if (![name isKindOfClass:[NSString class]]) continue;
/* In theory, we could use -fileExists:inFolder:referencedFrom:checkBuiltIn:
here, but we want a different error message.
*/
lcName = [name lowercaseString];
realFileName = [[_directoryListings objectForKey:@"config"] objectForKey:lcName];
inConfigDir = realFileName != nil;
if (!inConfigDir) realFileName = [[_directoryListings objectForKey:@""] objectForKey:lcName];
if (realFileName == nil) continue;
if (![realFileName isEqualToString:name])
{
if (inConfigDir) realFileName = [@"Config" stringByAppendingPathComponent:realFileName];
OOLog(@"verifyOXP.files.caseMismatch", @"ERROR: Case mismatch: configuration file \"%@\" should be called \"%@\".", realFileName, name);
}
}
}
- (NSDictionary *)lowercaseMap:(NSArray *)array
{
unsigned i, count;
NSString *canonical = nil,
*lowercase = nil;
NSMutableDictionary *result = nil;
count = [array count];
if (count == 0) return [NSDictionary dictionary];
result = [NSMutableDictionary dictionaryWithCapacity:count];
for (i = 0; i != count; ++i)
{
canonical = [array stringAtIndex:i];
if (canonical != nil)
{
lowercase = [canonical lowercaseString];
[result setObject:canonical forKey:lowercase];
}
}
return result;
}
- (NSDictionary *)scanDirectory:(NSString *)path
{
NSDirectoryEnumerator *dirEnum = nil;
NSMutableDictionary *result = nil;
NSString *name = nil,
*lcName = nil,
*type = nil,
*dirName = nil,
*relativeName = nil,
*existing = nil;
result = [NSMutableDictionary dictionary];
dirName = [path lastPathComponent];
dirEnum = [[NSFileManager defaultManager] enumeratorAtPath:path];
while ((name = [dirEnum nextObject]))
{
type = [[dirEnum fileAttributes] fileType];
relativeName = [dirName stringByAppendingPathComponent:name];
if ([type isEqualToString:NSFileTypeRegular])
{
lcName = [name lowercaseString];
existing = [result objectForKey:lcName];
if (existing == nil)
{
[result setObject:name forKey:lcName];
}
else
{
OOLog(@"verifyOXP.scanFiles.overloadedName", @"ERROR: %@ named \"%@\" conflicts with %@ named \"%@\", ignoring. (OXPs must work on case-insensitive file systems!)", @"file", relativeName, @"file", [dirName stringByAppendingPathComponent:existing]);
}
}
else
{
if ([type isEqualToString:NSFileTypeSymbolicLink])
{
[dirEnum skipDescendents];
OOLog(@"verifyOXP.scanFiles.symLink", @"WARNING: \"%@\" is a nested directory, ignoring.", relativeName);
}
else if ([type isEqualToString:NSFileTypeSymbolicLink])
{
OOLog(@"verifyOXP.scanFiles.symLink", @"WARNING: \"%@\" is a symbolic link, ignoring.", relativeName);
}
else
{
OOLog(@"verifyOXP.scanFiles.nonStandardFile", @"WARNING: \"%@\" is a non-standard file (%@), ignoring.", name, relativeName);
}
}
}
return result;
}
@end
static BOOL CheckNameConflict(NSString *lcName, NSDictionary *directoryCases, NSDictionary *rootFiles, NSString **outExisting, NSString **outExistingType)
{
NSString *existing = nil;
existing = [directoryCases objectForKey:lcName];
if (existing != nil)
{
if (outExisting != NULL) *outExisting = existing;
if (outExistingType != NULL) *outExisting = @"directory";
return YES;
}
existing = [rootFiles objectForKey:lcName];
if (existing != nil)
{
if (outExisting != NULL) *outExisting = existing;
if (outExistingType != NULL) *outExisting = @"file";
return YES;
}
return NO;
}
#endif

View File

@ -291,6 +291,7 @@ static void NoteVerificationStage(NSString *displayName, NSString *stage);
[ResourceManager setUseAddOns:NO];
SwitchLogFileAndOpenLog(_displayName);
OOLog(@"verifyOXP.start", @"Running OXP verifier for %@", _displayName);
[self registerBaseStages];
_openForRegistration = NO;

View File

@ -553,4 +553,4 @@ static JSBool QuaternionVectorRight(JSContext *context, JSObject *this, uintN ar
result = vector_right_from_quaternion(thisq);
return VectorToJSValue(context, result, outResult);
}
}