Line data Source code
1 0 : /*
2 :
3 : OOFileScannerVerifierStage.m
4 :
5 :
6 : Copyright (C) 2007-2013 Jens Ayton
7 :
8 : Permission is hereby granted, free of charge, to any person obtaining a copy
9 : of this software and associated documentation files (the "Software"), to deal
10 : in the Software without restriction, including without limitation the rights
11 : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 : copies of the Software, and to permit persons to whom the Software is
13 : furnished to do so, subject to the following conditions:
14 :
15 : The above copyright notice and this permission notice shall be included in all
16 : copies or substantial portions of the Software.
17 :
18 : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 : SOFTWARE.
25 :
26 : */
27 :
28 :
29 : /* Design notes:
30 : In order to be able to look files up case-insenstively, but warn about
31 : case mismatches, the OOFileScannerVerifierStage builds its own
32 : representation of the file hierarchy. Dictionaries are used heavily: the
33 : _directoryListings is keyed by folder names mapped to lower case, and its
34 : entries map lowercase file names to actual case, that is, the case found
35 : in the file system. The companion dictionary _directoryCases maps
36 : lowercase directory names to actual case.
37 :
38 : The class design is based on the knowledge that Oolite uses a two-level
39 : namespace for files. Each file type has an appropriate folder, and files
40 : may either be in the appropriate folder or "bare". For instance, a texture
41 : file in an OXP may be either in the Textures subdirectory or in the root
42 : directory of the OXP. The root directory's contents are listed in
43 : _directoryListings with the empty string as key. This architecture means
44 : the OOFileScannerVerifierStage doesn't need to take full file system
45 : hierarchy into account.
46 : */
47 :
48 : #import "OOFileScannerVerifierStage.h"
49 :
50 : #if OO_OXP_VERIFIER_ENABLED
51 :
52 : #import "OOCollectionExtractors.h"
53 : #import "ResourceManager.h"
54 :
55 0 : static NSString * const kFileScannerStageName = @"Scanning files";
56 0 : static NSString * const kUnusedListerStageName = @"Checking for unused files";
57 :
58 :
59 : static BOOL CheckNameConflict(NSString *lcName, NSDictionary *directoryCases, NSDictionary *rootFiles, NSString **outExisting, NSString **outExistingType);
60 :
61 :
62 : @interface OOFileScannerVerifierStage (OOPrivate)
63 :
64 0 : - (void)scanForFiles;
65 :
66 0 : - (void)checkRootFolders;
67 0 : - (void)checkKnownFiles;
68 :
69 : /* Given an array of strings, return a dictionary mapping lowercase strings
70 : to the canonicial case given in the array. For instance, given
71 : (Foo, BAR)
72 :
73 : it will return
74 : { foo = Foo; bar = BAR }
75 : */
76 0 : - (NSDictionary *)lowercaseMap:(NSArray *)array;
77 :
78 0 : - (NSDictionary *)scanDirectory:(NSString *)path;
79 0 : - (void)checkPListFormat:(NSPropertyListFormat)format file:(NSString *)file folder:(NSString *)folder;
80 0 : - (NSSet *)constructReadMeNames;
81 :
82 : @end
83 :
84 :
85 : @implementation OOFileScannerVerifierStage
86 :
87 0 : - (void)dealloc
88 : {
89 : [_basePath release];
90 : [_usedFiles release];
91 : [_caseWarnings release];
92 : [_directoryListings release];
93 : [_directoryCases release];
94 : [_badPLists release];
95 :
96 : [super dealloc];
97 : }
98 :
99 :
100 0 : - (NSString *)name
101 : {
102 : return kFileScannerStageName;
103 : }
104 :
105 :
106 0 : - (void)run
107 : {
108 : NSAutoreleasePool *pool = nil;
109 :
110 : _usedFiles = [[NSMutableSet alloc] init];
111 : _caseWarnings = [[NSMutableSet alloc] init];
112 : _badPLists = [[NSMutableSet alloc] init];
113 :
114 : pool = [[NSAutoreleasePool alloc] init];
115 : [self scanForFiles];
116 : [pool release];
117 :
118 : pool = [[NSAutoreleasePool alloc] init];
119 : [self checkRootFolders];
120 : [self checkKnownFiles];
121 : [pool release];
122 : }
123 :
124 :
125 : + (NSString *)nameForDependencyForVerifier:(OOOXPVerifier *)verifier
126 : {
127 : OOFileScannerVerifierStage *stage = [verifier stageWithName:kFileScannerStageName];
128 : if (stage == nil)
129 : {
130 : stage = [[OOFileScannerVerifierStage alloc] init];
131 : [verifier registerStage:stage];
132 : [stage release];
133 : }
134 :
135 : return kFileScannerStageName;
136 : }
137 :
138 :
139 : - (BOOL)fileExists:(NSString *)file
140 : inFolder:(NSString *)folder
141 : referencedFrom:(NSString *)context
142 : checkBuiltIn:(BOOL)checkBuiltIn
143 : {
144 : return [self pathForFile:file inFolder:folder referencedFrom:context checkBuiltIn:checkBuiltIn] != nil;
145 : }
146 :
147 :
148 : - (NSString *)pathForFile:(NSString *)file
149 : inFolder:(NSString *)folder
150 : referencedFrom:(NSString *)context
151 : checkBuiltIn:(BOOL)checkBuiltIn
152 : {
153 : NSString *lcName = nil,
154 : *lcDirName = nil,
155 : *realDirName = nil,
156 : *realFileName = nil,
157 : *path = nil,
158 : *expectedPath = nil;
159 :
160 : if (file == nil) return nil;
161 : lcName = [file lowercaseString];
162 :
163 : if (folder != nil)
164 : {
165 : lcDirName = [folder lowercaseString];
166 : realFileName = [[_directoryListings oo_dictionaryForKey:lcDirName] objectForKey:lcName];
167 :
168 : if (realFileName != nil)
169 : {
170 : realDirName = [_directoryCases objectForKey:lcDirName];
171 : path = [realDirName stringByAppendingPathComponent:realFileName];
172 : }
173 : }
174 :
175 : if (path == nil)
176 : {
177 : realFileName = [[_directoryListings oo_dictionaryForKey:@""] objectForKey:lcName];
178 :
179 : if (realFileName != nil)
180 : {
181 : path = realFileName;
182 : }
183 : }
184 :
185 : if (path != nil)
186 : {
187 : [_usedFiles addObject:path];
188 : if (realDirName != nil && ![realDirName isEqual:folder])
189 : {
190 : // Case mismatch for folder name
191 : if (![_caseWarnings containsObject:lcDirName])
192 : {
193 : [_caseWarnings addObject:lcDirName];
194 : OOLog(@"verifyOXP.files.caseMismatch", @"***** ERROR: case mismatch: directory '%@' should be called '%@'.", realDirName, folder);
195 : }
196 : }
197 :
198 : if (![realFileName isEqual:file])
199 : {
200 : // Case mismatch for file name
201 : if (![_caseWarnings containsObject:lcName])
202 : {
203 : [_caseWarnings addObject:lcName];
204 :
205 : expectedPath = [self displayNameForFile:file andFolder:folder];
206 :
207 : if (context != nil) context = [@" referenced in " stringByAppendingString:context];
208 : else context = @"";
209 :
210 : OOLog(@"verifyOXP.files.caseMismatch", @"***** ERROR: case mismatch: request for file '%@'%@ resolved to '%@'.", expectedPath, context, path);
211 : }
212 : }
213 :
214 : return [_basePath stringByAppendingPathComponent:path];
215 : }
216 :
217 : // If we get here, the file wasn't found in the OXP.
218 : // FIXME: should check case for built-in files.
219 : if (checkBuiltIn) return [ResourceManager pathForFileNamed:file inFolder:folder];
220 :
221 : return nil;
222 : }
223 :
224 :
225 : - (NSData *)dataForFile:(NSString *)file
226 : inFolder:(NSString *)folder
227 : referencedFrom:(NSString *)context
228 : checkBuiltIn:(BOOL)checkBuiltIn
229 : {
230 : NSString *path = nil;
231 :
232 : path = [self pathForFile:file inFolder:folder referencedFrom:context checkBuiltIn:checkBuiltIn];
233 : if (path == nil) return nil;
234 :
235 : return [NSData dataWithContentsOfMappedFile:path];
236 : }
237 :
238 :
239 : - (id)plistNamed:(NSString *)file
240 : inFolder:(NSString *)folder
241 : referencedFrom:(NSString *)context
242 : checkBuiltIn:(BOOL)checkBuiltIn
243 : {
244 : NSData *data = nil;
245 : NSString *errorString = nil;
246 : NSPropertyListFormat format;
247 : id plist = nil;
248 : NSArray *errorLines = nil;
249 : NSEnumerator *errLineEnum = nil;
250 : NSString *displayName = nil,
251 : *errorKey = nil;
252 : NSAutoreleasePool *pool = nil;
253 :
254 : data = [self dataForFile:file inFolder:folder referencedFrom:context checkBuiltIn:checkBuiltIn];
255 : if (data == nil) return nil;
256 :
257 : pool = [[NSAutoreleasePool alloc] init];
258 :
259 : plist = [NSPropertyListSerialization propertyListFromData:data
260 : mutabilityOption:NSPropertyListImmutable
261 : format:&format
262 : errorDescription:&errorString];
263 :
264 : #if OOLITE_RELEASE_PLIST_ERROR_STRINGS
265 : [errorString autorelease];
266 : #endif
267 :
268 : if (plist != nil)
269 : {
270 : // PList is readable; check that it's in an official Oolite format.
271 : [self checkPListFormat:format file:file folder:folder];
272 : }
273 : else
274 : {
275 : /* Couldn't parse plist; report problem.
276 : This is complicated somewhat by the need to present a possibly
277 : multi-line error description while maintaining our indentation.
278 : */
279 : displayName = [self displayNameForFile:file andFolder:folder];
280 : errorKey = [displayName lowercaseString];
281 : if (![_badPLists containsObject:errorKey])
282 : {
283 : [_badPLists addObject:errorKey];
284 : OOLog(@"verifyOXP.plist.parseError", @"Could not interpret property list %@.", displayName);
285 : OOLogIndent();
286 : errorLines = [errorString componentsSeparatedByString:@"\n"];
287 : for (errLineEnum = [errorLines objectEnumerator]; (errorString = [errLineEnum nextObject]); )
288 : {
289 : while ([errorString hasPrefix:@"\t"])
290 : {
291 : errorString = [@" " stringByAppendingString:[errorString substringFromIndex:1]];
292 : }
293 : OOLog(@"verifyOXP.plist.parseError", @"%@", errorString);
294 : }
295 : OOLogOutdent();
296 : }
297 : }
298 :
299 : [plist retain];
300 : [pool release];
301 :
302 : return [plist autorelease];
303 : }
304 :
305 :
306 : - (id)displayNameForFile:(NSString *)file andFolder:(NSString *)folder
307 : {
308 : if (file != nil && folder != nil) return [folder stringByAppendingPathComponent:file];
309 : return file;
310 : }
311 :
312 :
313 : - (NSArray *)filesInFolder:(NSString *)folder
314 : {
315 : if (folder == nil) return nil;
316 : return [[_directoryListings objectForKey:[folder lowercaseString]] allValues];
317 : }
318 :
319 : @end
320 :
321 :
322 : @implementation OOFileScannerVerifierStage (OOPrivate)
323 :
324 : - (void)scanForFiles
325 : {
326 : NSDirectoryEnumerator *dirEnum = nil;
327 : NSString *name = nil,
328 : *path = nil,
329 : *type = nil,
330 : *lcName = nil,
331 : *existing = nil,
332 : *existingType = nil;
333 : NSMutableDictionary *directoryListings = nil,
334 : *directoryCases = nil,
335 : *rootFiles = nil;
336 : NSDictionary *dirFiles = nil;
337 : NSSet *readMeNames = nil;
338 :
339 : _basePath = [[[self verifier] oxpPath] copy];
340 :
341 : _junkFileNames = [[self verifier] configurationSetForKey:@"junkFiles"];
342 : _skipDirectoryNames = [[self verifier] configurationSetForKey:@"skipDirectories"];
343 :
344 : directoryCases = [NSMutableDictionary dictionary];
345 : directoryListings = [NSMutableDictionary dictionary];
346 : rootFiles = [NSMutableDictionary dictionary];
347 : readMeNames = [self constructReadMeNames];
348 :
349 : dirEnum = [[NSFileManager defaultManager] enumeratorAtPath:_basePath];
350 : while ((name = [dirEnum nextObject]))
351 : {
352 : path = [_basePath stringByAppendingPathComponent:name];
353 : type = [[dirEnum fileAttributes] fileType];
354 : lcName = [name lowercaseString];
355 :
356 : if ([type isEqualToString:NSFileTypeDirectory])
357 : {
358 : [dirEnum skipDescendents];
359 :
360 : if ([_skipDirectoryNames containsObject:name])
361 : {
362 : // Silently skip .svn and CVS
363 : OOLog(@"verifyOXP.verbose.listFiles", @"- Skipping %@/", name);
364 : }
365 : else if (!CheckNameConflict(lcName, directoryCases, rootFiles, &existing, &existingType))
366 : {
367 : OOLog(@"verifyOXP.verbose.listFiles", @"- %@/", name);
368 : OOLogIndentIf(@"verifyOXP.verbose.listFiles");
369 : dirFiles = [self scanDirectory:path];
370 : [directoryListings setObject:dirFiles forKey:lcName];
371 : [directoryCases setObject:name forKey:lcName];
372 : OOLogOutdentIf(@"verifyOXP.verbose.listFiles");
373 : }
374 : else
375 : {
376 : OOLog(@"verifyOXP.scanFiles.overloadedName", @"***** ERROR: %@ '%@' conflicts with %@ named '%@', ignoring. (OXPs must work on case-insensitive file systems!)", @"directory", name, existingType, existing);
377 : }
378 : }
379 : else if ([type isEqualToString:NSFileTypeRegular])
380 : {
381 : if ([_junkFileNames containsObject:name])
382 : {
383 : OOLog(@"verifyOXP.scanFiles.skipJunk", @"NOTE: skipping junk file %@.", name);
384 : }
385 : else if ([readMeNames containsObject:lcName])
386 : {
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);
388 : }
389 : else if (!CheckNameConflict(lcName, directoryCases, rootFiles, &existing, &existingType))
390 : {
391 : OOLog(@"verifyOXP.verbose.listFiles", @"- %@", name);
392 : [rootFiles setObject:name forKey:lcName];
393 : }
394 : else
395 : {
396 : OOLog(@"verifyOXP.scanFiles.overloadedName", @"***** ERROR: %@ '%@' conflicts with %@ named '%@', ignoring. (OXPs must work on case-insensitive file systems!)", @"file", name, existingType, existing);
397 : }
398 : }
399 : else if ([type isEqualToString:NSFileTypeSymbolicLink])
400 : {
401 : OOLog(@"verifyOXP.scanFiles.symLink", @"----- WARNING: \"%@\" is a symbolic link, ignoring.", name);
402 : }
403 : else
404 : {
405 : OOLog(@"verifyOXP.scanFiles.nonStandardFile", @"----- WARNING: \"%@\" is a non-standard file (%@), ignoring.", name, type);
406 : }
407 : }
408 :
409 : _junkFileNames = nil;
410 : _skipDirectoryNames = nil;
411 :
412 : [directoryListings setObject:rootFiles forKey:@""];
413 : _directoryListings = [directoryListings copy];
414 : _directoryCases = [directoryCases copy];
415 : }
416 :
417 :
418 : - (void)checkRootFolders
419 : {
420 : NSArray *knownNames = nil;
421 : NSEnumerator *nameEnum = nil;
422 : NSString *name = nil;
423 : NSString *lcName = nil;
424 : NSString *actual = nil;
425 :
426 : knownNames = [[self verifier] configurationArrayForKey:@"knownRootDirectories"];
427 : for (nameEnum = [knownNames objectEnumerator]; (name = [nameEnum nextObject]); )
428 : {
429 : if (![name isKindOfClass:[NSString class]]) continue;
430 :
431 : lcName = [name lowercaseString];
432 : actual = [_directoryCases objectForKey:lcName];
433 : if (actual == nil) continue;
434 :
435 : if (![actual isEqualToString:name])
436 : {
437 : OOLog(@"verifyOXP.files.caseMismatch", @"***** ERROR: case mismatch: directory '%@' should be called '%@'.", actual, name);
438 : }
439 : [_caseWarnings addObject:lcName];
440 : }
441 : }
442 :
443 :
444 0 : - (void)checkConfigFiles
445 : {
446 : NSArray *knownNames = nil;
447 : NSEnumerator *nameEnum = nil;
448 : NSString *name = nil,
449 : *lcName = nil,
450 : *realFileName = nil;
451 : BOOL inConfigDir;
452 :
453 : knownNames = [[self verifier] configurationArrayForKey:@"knownConfigFiles"];
454 : for (nameEnum = [knownNames objectEnumerator]; (name = [nameEnum nextObject]); )
455 : {
456 : if (![name isKindOfClass:[NSString class]]) continue;
457 :
458 : /* In theory, we could use -fileExists:inFolder:referencedFrom:checkBuiltIn:
459 : here, but we want a different error message.
460 : */
461 :
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;
467 :
468 : if (![realFileName isEqualToString:name])
469 : {
470 : if (inConfigDir) realFileName = [@"Config" stringByAppendingPathComponent:realFileName];
471 : OOLog(@"verifyOXP.files.caseMismatch", @"***** ERROR: case mismatch: configuration file '%@' should be called '%@'.", realFileName, name);
472 : }
473 : }
474 : }
475 :
476 :
477 : - (void)checkKnownFiles
478 : {
479 : NSDictionary *directories = nil;
480 : NSEnumerator *directoryEnum = nil;
481 : NSString *directory = nil,
482 : *lcDirectory = nil;
483 : NSArray *fileList = nil;
484 : NSEnumerator *nameEnum = nil;
485 : NSString *name = nil,
486 : *lcName = nil,
487 : *realFileName = nil;
488 : BOOL inDirectory;
489 :
490 : directories = [[self verifier] configurationDictionaryForKey:@"knownFiles"];
491 : for (directoryEnum = [directories keyEnumerator]; (directory = [directoryEnum nextObject]); )
492 : {
493 : fileList = [directories objectForKey:directory];
494 : lcDirectory = [directory lowercaseString];
495 : for (nameEnum = [fileList objectEnumerator]; (name = [nameEnum nextObject]); )
496 : {
497 : if (![name isKindOfClass:[NSString class]]) continue;
498 :
499 : /* In theory, we could use -fileExists:inFolder:referencedFrom:checkBuiltIn:
500 : here, but we want a different error message.
501 : */
502 :
503 : lcName = [name lowercaseString];
504 : realFileName = [[_directoryListings oo_dictionaryForKey:lcDirectory] objectForKey:lcName];
505 : inDirectory = (realFileName != nil);
506 : if (!inDirectory)
507 : {
508 : // Allow for files in root directory of OXP
509 : realFileName = [[_directoryListings oo_dictionaryForKey:@""] objectForKey:lcName];
510 : }
511 : if (realFileName == nil) continue;
512 :
513 : if (![realFileName isEqualToString:name])
514 : {
515 : if (inDirectory) realFileName = [directory stringByAppendingPathComponent:realFileName];
516 : OOLog(@"verifyOXP.files.caseMismatch", @"***** ERROR: case mismatch: file '%@' should be called '%@'.", realFileName, name);
517 : }
518 : }
519 : }
520 : }
521 :
522 :
523 : - (NSDictionary *)lowercaseMap:(NSArray *)array
524 : {
525 : NSUInteger i, count;
526 : NSString *canonical = nil,
527 : *lowercase = nil;
528 : NSMutableDictionary *result = nil;
529 :
530 : count = [array count];
531 : if (count == 0) return [NSDictionary dictionary];
532 : result = [NSMutableDictionary dictionaryWithCapacity:count];
533 :
534 : for (i = 0; i != count; ++i)
535 : {
536 : canonical = [array oo_stringAtIndex:i];
537 : if (canonical != nil)
538 : {
539 : lowercase = [canonical lowercaseString];
540 : [result setObject:canonical forKey:lowercase];
541 : }
542 : }
543 :
544 : return result;
545 : }
546 :
547 :
548 : - (NSDictionary *)scanDirectory:(NSString *)path
549 : {
550 : NSDirectoryEnumerator *dirEnum = nil;
551 : NSMutableDictionary *result = nil;
552 : NSString *name = nil,
553 : *lcName = nil,
554 : *type = nil,
555 : *dirName = nil,
556 : *relativeName = nil,
557 : *existing = nil;
558 :
559 : result = [NSMutableDictionary dictionary];
560 : dirName = [path lastPathComponent];
561 :
562 : dirEnum = [[NSFileManager defaultManager] enumeratorAtPath:path];
563 : while ((name = [dirEnum nextObject]))
564 : {
565 : type = [[dirEnum fileAttributes] fileType];
566 : relativeName = [dirName stringByAppendingPathComponent:name];
567 :
568 : if ([_junkFileNames containsObject:name])
569 : {
570 : OOLog(@"verifyOXP.scanFiles.skipJunk", @"NOTE: skipping junk file %@/%@.", dirName, name);
571 : }
572 : else if ([type isEqualToString:NSFileTypeRegular])
573 : {
574 : lcName = [name lowercaseString];
575 : existing = [result objectForKey:lcName];
576 :
577 : if (existing == nil)
578 : {
579 : OOLog(@"verifyOXP.verbose.listFiles", @"- %@", name);
580 : [result setObject:name forKey:lcName];
581 : }
582 : else
583 : {
584 : OOLog(@"verifyOXP.scanFiles.overloadedName", @"***** ERROR: %@ '%@' conflicts with %@ named '%@', ignoring. (OXPs must work on case-insensitive file systems!)", @"file", relativeName, @"file", [dirName stringByAppendingPathComponent:existing]);
585 : }
586 : }
587 : else
588 : {
589 : if ([type isEqualToString:NSFileTypeDirectory])
590 : {
591 : [dirEnum skipDescendents];
592 : if (![_skipDirectoryNames containsObject:name])
593 : {
594 : OOLog(@"verifyOXP.scanFiles.directory", @"----- WARNING: \"%@\" is a nested directory, ignoring.", relativeName);
595 : }
596 : else
597 : {
598 : OOLog(@"verifyOXP.verbose.listFiles", @"- Skipping %@/%@/", dirName, name);
599 : }
600 : }
601 : else if ([type isEqualToString:NSFileTypeSymbolicLink])
602 : {
603 : OOLog(@"verifyOXP.scanFiles.symLink", @"----- WARNING: \"%@\" is a symbolic link, ignoring.", relativeName);
604 : }
605 : else
606 : {
607 : OOLog(@"verifyOXP.scanFiles.nonStandardFile", @"----- WARNING: \"%@\" is a non-standard file (%@), ignoring.", relativeName, type);
608 : }
609 : }
610 : }
611 :
612 : return result;
613 : }
614 :
615 :
616 : - (void)checkPListFormat:(NSPropertyListFormat)format file:(NSString *)file folder:(NSString *)folder
617 : {
618 : NSString *weirdnessKey = nil;
619 : NSString *formatDesc = nil;
620 : NSString *displayPath = nil;
621 :
622 : if (format != NSPropertyListOpenStepFormat && format != NSPropertyListXMLFormat_v1_0)
623 : {
624 : displayPath = [self displayNameForFile:file andFolder:folder];
625 : weirdnessKey = [displayPath lowercaseString];
626 :
627 : if (![_badPLists containsObject:weirdnessKey])
628 : {
629 : // Warn about "non-standard" format
630 : [_badPLists addObject:weirdnessKey];
631 :
632 : switch (format)
633 : {
634 : case NSPropertyListBinaryFormat_v1_0:
635 : formatDesc = @"Apple binary format";
636 : break;
637 :
638 : #if OOLITE_GNUSTEP
639 : case NSPropertyListGNUstepFormat:
640 : formatDesc = @"GNUstep text format";
641 : break;
642 :
643 : case NSPropertyListGNUstepBinaryFormat:
644 : formatDesc = @"GNUstep binary format";
645 : break;
646 : #endif
647 :
648 : default:
649 : formatDesc = [NSString stringWithFormat:@"unknown format (%i)", (int)format];
650 : }
651 :
652 : OOLog(@"verifyOXP.plist.weirdFormat", @"----- WARNING: Property list %@ is in %@; OpenStep text format and XML format are the recommended formats for Oolite.", displayPath, formatDesc);
653 : }
654 : }
655 : }
656 :
657 :
658 0 : - (NSSet *)constructReadMeNames
659 : {
660 : NSDictionary *dict = nil;
661 : NSArray *stems = nil,
662 : *extensions = nil;
663 : NSMutableSet *result = nil;
664 : NSUInteger i, j, stemCount, extCount;
665 : NSString *stem = nil,
666 : *extension = nil;
667 :
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;
674 :
675 : // Construct all stem+extension permutations
676 : result = [NSMutableSet setWithCapacity:stemCount * extCount];
677 : for (i = 0; i != stemCount; ++i)
678 : {
679 : stem = [[stems oo_stringAtIndex:i] lowercaseString];
680 : if (stem != nil)
681 : {
682 : for (j = 0; j != extCount; ++j)
683 : {
684 : extension = [[extensions oo_stringAtIndex:j] lowercaseString];
685 : if (extension != nil)
686 : {
687 : [result addObject:[stem stringByAppendingString:extension]];
688 : }
689 : }
690 : }
691 : }
692 :
693 : return result;
694 : }
695 :
696 : @end
697 :
698 :
699 : @implementation OOListUnusedFilesStage: OOOXPVerifierStage
700 :
701 : - (NSString *)name
702 : {
703 : return kUnusedListerStageName;
704 : }
705 :
706 :
707 0 : - (NSSet *)dependencies
708 : {
709 : return [NSSet setWithObject:kFileScannerStageName];
710 : }
711 :
712 :
713 0 : - (void)run
714 : {
715 : OOLog(@"verifyOXP.unusedFiles.unimplemented", @"%@", @"TODO: implement unused files check.");
716 : }
717 :
718 :
719 : + (NSString *)nameForReverseDependencyForVerifier:(OOOXPVerifier *)verifier
720 : {
721 : OOListUnusedFilesStage *stage = [verifier stageWithName:kUnusedListerStageName];
722 : if (stage == nil)
723 : {
724 : stage = [[OOListUnusedFilesStage alloc] init];
725 : [verifier registerStage:stage];
726 : [stage release];
727 : }
728 :
729 : return kUnusedListerStageName;
730 : }
731 :
732 : @end
733 :
734 :
735 : @implementation OOOXPVerifier(OOFileScannerVerifierStage)
736 :
737 : - (OOFileScannerVerifierStage *)fileScannerStage
738 : {
739 : return [self stageWithName:kFileScannerStageName];
740 : }
741 :
742 : @end
743 :
744 :
745 : @implementation OOFileHandlingVerifierStage
746 :
747 0 : - (NSSet *)dependencies
748 : {
749 : return [NSSet setWithObject:[OOFileScannerVerifierStage nameForDependencyForVerifier:[self verifier]]];
750 : }
751 :
752 :
753 0 : - (NSSet *)dependents
754 : {
755 : return [NSSet setWithObject:[OOListUnusedFilesStage nameForReverseDependencyForVerifier:[self verifier]]];
756 : }
757 :
758 : @end
759 :
760 :
761 0 : static BOOL CheckNameConflict(NSString *lcName, NSDictionary *directoryCases, NSDictionary *rootFiles, NSString **outExisting, NSString **outExistingType)
762 : {
763 : NSString *existing = nil;
764 :
765 : existing = [directoryCases objectForKey:lcName];
766 : if (existing != nil)
767 : {
768 : if (outExisting != NULL) *outExisting = existing;
769 : if (outExistingType != NULL) *outExistingType = @"directory";
770 : return YES;
771 : }
772 :
773 : existing = [rootFiles objectForKey:lcName];
774 : if (existing != nil)
775 : {
776 : if (outExisting != NULL) *outExisting = existing;
777 : if (outExistingType != NULL) *outExistingType = @"file";
778 : return YES;
779 : }
780 :
781 : return NO;
782 : }
783 :
784 : #endif
|