LCOV - code coverage report
Current view: top level - Core - OOLogOutputHandler.m (source / functions) Hit Total Coverage
Test: coverxygen.info Lines: 0 49 0.0 %
Date: 2026-01-23 10:53:51 Functions: 0 0 -

          Line data    Source code
       1           0 : /*
       2             : 
       3             : OOLogOutputHandler.m
       4             : By Jens Ayton
       5             : 
       6             : 
       7             : Copyright (C) 2007-2013 Jens Ayton and contributors
       8             : 
       9             : Permission is hereby granted, free of charge, to any person obtaining a copy
      10             : of this software and associated documentation files (the "Software"), to deal
      11             : in the Software without restriction, including without limitation the rights
      12             : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
      13             : copies of the Software, and to permit persons to whom the Software is
      14             : furnished to do so, subject to the following conditions:
      15             : 
      16             : The above copyright notice and this permission notice shall be included in all
      17             : copies or substantial portions of the Software.
      18             : 
      19             : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      20             : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      21             : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
      22             : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      23             : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
      24             : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
      25             : SOFTWARE.
      26             : 
      27             : */
      28             : 
      29             : 
      30           0 : #define OOLOG_POISON_NSLOG 0
      31             : 
      32             : #import "OOLogOutputHandler.h"
      33             : #import "OOLogging.h"
      34             : #import "OOAsyncQueue.h"
      35             : #include <stdlib.h>
      36             : #include <stdio.h>
      37             : #import "NSThreadOOExtensions.h"
      38             : #import "NSFileManagerOOExtensions.h"
      39             : #include <SDL.h>
      40             : 
      41             : 
      42             : #undef NSLog            // We need to be able to call the real NSLog.
      43             : 
      44             : 
      45             : #if OOLITE_MAC_OS_X
      46             : 
      47             : #include <dlfcn.h>
      48             : 
      49             : #ifndef NDEBUG
      50             : 
      51           0 : #define SET_CRASH_REPORTER_INFO 1
      52             : 
      53             : // Function to set "Application Specific Information" field in crash reporter log in Leopard.
      54             : // Extremely unsupported, so not used in release builds.
      55             : static void InitCrashReporterInfo(void);
      56             : static void SetCrashReporterInfo(const char *info);
      57           0 : static BOOL sCrashReporterInfoAvailable = NO;
      58             : 
      59             : #endif
      60             : 
      61             : 
      62           0 : typedef void (*LogCStringFunctionProc)(const char *string, unsigned length, BOOL withSyslogBanner);
      63           0 : typedef LogCStringFunctionProc (*LogCStringFunctionGetterProc)(void);
      64           0 : typedef void (*LogCStringFunctionSetterProc)(LogCStringFunctionProc);
      65             : 
      66           0 : static LogCStringFunctionGetterProc _NSLogCStringFunction = NULL;
      67           0 : static LogCStringFunctionSetterProc _NSSetLogCStringFunction = NULL;
      68             : 
      69             : static void LoadLogCStringFunctions(void);
      70             : static void OONSLogCStringFunction(const char *string, unsigned length, BOOL withSyslogBanner);
      71             : 
      72             : static NSString *GetAppName(void);
      73             : 
      74           0 : static LogCStringFunctionProc   sDefaultLogCStringFunction = NULL;
      75             : 
      76             : #elif OOLITE_GNUSTEP
      77             : 
      78             : static void OONSLogPrintfHandler(NSString *message);
      79             : 
      80             : #else
      81             : #error Unknown platform!
      82             : #endif
      83             : 
      84             : static BOOL DirectoryExistCreatingIfNecessary(NSString *path);
      85             : 
      86             : 
      87           0 : #define kFlushInterval  2.0             // Lower bound on interval between explicit log file flushes.
      88             : 
      89             : 
      90           0 : @interface OOAsyncLogger: NSObject
      91             : {
      92             : @private
      93           0 :         OOAsyncQueue            *messageQueue;
      94           0 :         NSConditionLock         *threadStateMonitor;
      95           0 :         NSFileHandle            *logFile;
      96           0 :         NSTimer                         *flushTimer;
      97             : }
      98             : 
      99           0 : - (void)asyncLogMessage:(NSString *)message;
     100           0 : - (void)endLogging;
     101             : 
     102           0 : - (void)changeFile;
     103             : 
     104             : // Internal
     105           0 : - (BOOL)startLogging;
     106           0 : - (void)loggerThread;
     107           0 : - (void)flushLog;
     108             : 
     109             : @end
     110             : 
     111             : 
     112           0 : static BOOL                                             sInited = NO;
     113           0 : static BOOL                                             sWriteToStderr = YES;
     114           0 : static BOOL                                             sWriteToStdout = NO;
     115           0 : static BOOL                                             sSaturated = NO;
     116           0 : static OOAsyncLogger                    *sLogger = nil;
     117           0 : static NSString                                 *sLogFileName = @"Latest.log";
     118             : 
     119             : 
     120           0 : void OOLogOutputHandlerInit(void)
     121             : {
     122             :         if (sInited)  return;
     123             :         
     124             : #if SET_CRASH_REPORTER_INFO
     125             :         InitCrashReporterInfo();
     126             : #endif
     127             :         
     128             :         sLogger = [[OOAsyncLogger alloc] init];
     129             :         sInited = YES;
     130             :         
     131             :         if (sLogger != nil)
     132             :         {
     133             :                 sWriteToStderr = [[NSUserDefaults standardUserDefaults] boolForKey:@"logging-echo-to-stderr"];
     134             :         }
     135             :         else
     136             :         {
     137             :                 sWriteToStderr = YES;
     138             :         }
     139             :         
     140             : #if OOLITE_MAC_OS_X
     141             :         LoadLogCStringFunctions();
     142             :         if (_NSSetLogCStringFunction != NULL)
     143             :         {
     144             :                 sDefaultLogCStringFunction = _NSLogCStringFunction();
     145             :                 _NSSetLogCStringFunction(OONSLogCStringFunction);
     146             :         }
     147             :         else
     148             :         {
     149             :                 OOLog(@"logging.nsLogFilter.install.failed", @"Failed to install NSLog() filter; system messages will not be logged in log file.");
     150             :         }
     151             : #elif GNUSTEP
     152             :         NSRecursiveLock *lock = GSLogLock();
     153             :         [lock lock];
     154             :         _NSLog_printf_handler = OONSLogPrintfHandler;
     155             :         [lock unlock];
     156             : #endif
     157             :         
     158             :         atexit(OOLogOutputHandlerClose);
     159             : }
     160             : 
     161             : 
     162           0 : void OOLogOutputHandlerClose(void)
     163             : {
     164             :         if (sInited)
     165             :         {
     166             :                 sWriteToStderr = YES;
     167             :                 sInited = NO;
     168             :                 
     169             :                 [sLogger endLogging];
     170             :                 DESTROY(sLogger);
     171             :                 
     172             : #if OOLITE_MAC_OS_X
     173             :                 if (sDefaultLogCStringFunction != NULL && _NSSetLogCStringFunction != NULL)
     174             :                 {
     175             :                         _NSSetLogCStringFunction(sDefaultLogCStringFunction);
     176             :                         sDefaultLogCStringFunction = NULL;
     177             :                 }
     178             : #elif GNUSTEP
     179             :                 NSRecursiveLock *lock = GSLogLock();
     180             :                 [lock lock];
     181             :                 _NSLog_printf_handler = NULL;
     182             :                 [lock unlock];
     183             : #endif
     184             :         }
     185             : }
     186             : 
     187           0 : void OOLogOutputHandlerStartLoggingToStdout()
     188             : {
     189             :         sWriteToStdout = true;
     190             : }
     191           0 : void OOLogOutputHandlerStopLoggingToStdout()
     192             : {
     193             :         sWriteToStdout = false;
     194             : }
     195             : 
     196           0 : void OOLogOutputHandlerPrint(NSString *string)
     197             : {
     198             :         if (sInited && sLogger != nil && !sWriteToStdout)  [sLogger asyncLogMessage:string];
     199             :         
     200             :         BOOL doCStringStuff = sWriteToStderr || sWriteToStdout;
     201             : #if SET_CRASH_REPORTER_INFO
     202             :         doCStringStuff = doCStringStuff || sCrashReporterInfoAvailable;
     203             : #endif
     204             :         
     205             :         if (doCStringStuff)
     206             :         {
     207             :                 const char *cStr = [[string stringByAppendingString:@"\n"] UTF8String];
     208             :                 if (sWriteToStdout)
     209             :                         fputs(cStr, stdout);
     210             :                 else if (sWriteToStderr)
     211             :                         fputs(cStr, stderr);
     212             :                 
     213             : #if SET_CRASH_REPORTER_INFO
     214             :                 if (sCrashReporterInfoAvailable)  SetCrashReporterInfo(cStr);
     215             : #endif
     216             :         }
     217             :         
     218             : }
     219             : 
     220             : 
     221           0 : NSString *OOLogHandlerGetLogPath(void)
     222             : {
     223             :         return [OOLogHandlerGetLogBasePath() stringByAppendingPathComponent:sLogFileName];      
     224             : }
     225             : 
     226             : 
     227           0 : void OOLogOutputHandlerChangeLogFile(NSString *newLogName)
     228             : {
     229             :         if (![sLogFileName isEqual:newLogName])
     230             :         {
     231             :                 sLogFileName = [newLogName copy];
     232             :                 [sLogger changeFile];
     233             :         }
     234             : }
     235             : 
     236             : 
     237           0 : enum
     238             : {
     239             :         kConditionReadyToDealloc = 1,
     240             :         kConditionWorking
     241             : };
     242             : 
     243             : 
     244             : @implementation OOAsyncLogger
     245             : 
     246           0 : - (id)init
     247             : {
     248             :         BOOL                            OK = YES;
     249             :         NSString                        *logPath = nil;
     250             :         NSString                        *oldPath = nil;
     251             :         NSFileManager           *fmgr = nil;
     252             :         
     253             :         self = [super init];
     254             :         if (self == nil)  OK = NO;
     255             :         
     256             :         if (OK)
     257             :         {
     258             :                 fmgr = [NSFileManager defaultManager];
     259             :                 logPath = OOLogHandlerGetLogPath();
     260             :                 
     261             :                 // If there is an existing file, move it to Previous.log.
     262             :                 if ([fmgr fileExistsAtPath:logPath])
     263             :                 {
     264             :                         oldPath = [OOLogHandlerGetLogBasePath() stringByAppendingPathComponent:@"Previous.log"];
     265             :                         [fmgr oo_removeItemAtPath:oldPath];
     266             :                         if (![fmgr oo_moveItemAtPath:logPath toPath:oldPath])
     267             :                         {
     268             :                                 if (![fmgr oo_removeItemAtPath:logPath])
     269             :                                 {
     270             :                                         NSLog(@"Log setup: could not move or delete existing log at %@, will log to stdout instead.", logPath);
     271             :                                         OK = NO;
     272             :                                 }
     273             :                         }
     274             :                 }
     275             :         }
     276             :         
     277             :         if (OK)  OK = [self startLogging];
     278             :         
     279             :         if (!OK)  DESTROY(self);
     280             :         
     281             :         return self;
     282             : }
     283             : 
     284             : 
     285           0 : - (void)dealloc
     286             : {
     287             :         DESTROY(messageQueue);
     288             :         DESTROY(threadStateMonitor);
     289             :         DESTROY(logFile);
     290             :         // We don't own a reference to flushTimer.
     291             :         
     292             :         [super dealloc];
     293             : }
     294             : 
     295             : 
     296             : - (BOOL)startLogging
     297             : {
     298             :         BOOL                            OK = YES;
     299             :         NSString                        *logPath = nil;
     300             :         NSFileManager           *fmgr = nil;
     301             :         
     302             :         fmgr = [NSFileManager defaultManager];
     303             :         
     304             :         if (OK)
     305             :         {
     306             :                 messageQueue = [[OOAsyncQueue alloc] init];
     307             :                 if (messageQueue == nil)  OK = NO;
     308             :         }
     309             :         
     310             :         if (OK)
     311             :         {
     312             :                 // set up threadStateMonitor -- used as a binary semaphore of sorts to check when the worker thread starts and stops.
     313             :                 threadStateMonitor = [[NSConditionLock alloc] initWithCondition:kConditionReadyToDealloc];
     314             :                 if (threadStateMonitor == nil)  OK = NO;
     315             :                 [threadStateMonitor setName:@"OOLogOutputHandler thread state monitor"];
     316             :         }
     317             :         
     318             :         if (OK)
     319             :         {
     320             :                 // Create work thread to actually handle messages.
     321             :                 // This needs to be done early to avoid messy state if something goes wrong.
     322             :                 [NSThread detachNewThreadSelector:@selector(loggerThread) toTarget:self withObject:nil];
     323             :                 // Wait for it to start.
     324             :                 if (![threadStateMonitor lockWhenCondition:kConditionWorking beforeDate:[NSDate dateWithTimeIntervalSinceNow:5.0]])
     325             :                 {
     326             :                         // If it doesn't signal a start within five seconds, assume something's wrong.
     327             :                         // Send kill signal, just in case it comes to life...
     328             :                         [messageQueue enqueue:@"die"];
     329             :                         // ...and stop -dealloc from waiting for thread death
     330             :                         [threadStateMonitor release];
     331             :                         threadStateMonitor = nil;
     332             :                         OK = NO;
     333             :                 }
     334             :                 [threadStateMonitor unlockWithCondition:kConditionWorking];
     335             :         }
     336             :         
     337             :         if (OK)
     338             :         {
     339             :                 logPath = OOLogHandlerGetLogPath();
     340             :                 OK = (logPath != nil);
     341             :         }
     342             :         
     343             :         if (OK)
     344             :         {
     345             :                 // Create shiny new log file
     346             :                 OK = [fmgr createFileAtPath:logPath contents:nil attributes:nil];
     347             :                 if (OK)
     348             :                 {
     349             :                         logFile = [[NSFileHandle fileHandleForWritingAtPath:logPath] retain];
     350             :                         OK = (logFile != nil);
     351             :                 }
     352             :                 if (!OK)
     353             :                 {
     354             :                         NSLog(@"Log setup: could not open log at %@, will log to stdout instead.", logPath);
     355             :                         OK = NO;
     356             :                 }
     357             :         }
     358             :         
     359             :         return OK;
     360             : }
     361             : 
     362             : 
     363             : - (void)endLogging
     364             : {
     365             :         NSString                                *postamble = nil;
     366             :         
     367             :         if (messageQueue != nil && threadStateMonitor != nil)
     368             :         {
     369             :                 // We're fully inited; write postamble, wait for worker thread to terminate cleanly, and close file.
     370             :                 postamble = [NSString stringWithFormat:@"\nClosing log at %@.", [NSDate date]];
     371             :                 [self asyncLogMessage:postamble];
     372             :                 [messageQueue enqueue:@"die"];        // Kill message
     373             :                 [threadStateMonitor lockWhenCondition:kConditionReadyToDealloc];
     374             :                 [threadStateMonitor unlock];
     375             :                 
     376             :                 [logFile closeFile];
     377             :         }
     378             : }
     379             : 
     380             : 
     381             : - (void)changeFile
     382             : {
     383             :         [self endLogging];
     384             :         if (![self startLogging])  sWriteToStderr = YES;
     385             : }
     386             : 
     387             : 
     388             : - (void)asyncLogMessage:(NSString *)message
     389             : {
     390             :         // Don't log of saturated flag is set.
     391             :         if (sSaturated)  return;
     392             :         
     393             :         if (message != nil)
     394             :         {
     395             :                 message = [message stringByAppendingString:@"\n"];
     396             :                 
     397             : #if OOLITE_WINDOWS
     398             :                 // Convert Unix line endings to Windows ones.
     399             :                 NSArray *messageComponents = [message componentsSeparatedByString:@"\n"];
     400             :                 message = [messageComponents componentsJoinedByString:@"\r\n"];
     401             : #endif
     402             :                 
     403             :                 [messageQueue enqueue:[message dataUsingEncoding:NSUTF8StringEncoding]];
     404             :                 
     405             :                 if (flushTimer == nil)
     406             :                 {
     407             :                         // No pending flush
     408             :                         flushTimer = [NSTimer scheduledTimerWithTimeInterval:kFlushInterval target:self selector:@selector(flushLog) userInfo:nil repeats:NO];
     409             :                 }
     410             :         }
     411             : }
     412             : 
     413             : 
     414             : - (void)flushLog
     415             : {
     416             :         flushTimer = nil;
     417             :         [messageQueue enqueue:@"flush"];
     418             : }
     419             : 
     420             : 
     421             : - (void)loggerThread
     422             : {
     423             :         id                                      message = nil;
     424             :         NSAutoreleasePool       *rootPool = nil, *pool = nil;
     425             :         NSUInteger                      size = 0;
     426             :         
     427             :         rootPool = [[NSAutoreleasePool alloc] init];
     428             :         [NSThread ooSetCurrentThreadName:@"OOLogOutputHandler logging thread"];
     429             :         
     430             :         // Signal readiness
     431             :         [messageQueue retain];
     432             :         [threadStateMonitor lock];
     433             :         [threadStateMonitor unlockWithCondition:kConditionWorking];
     434             :         
     435             :         @try
     436             :         {
     437             :                 for (;;)
     438             :                 {
     439             :                         pool = [[NSAutoreleasePool alloc] init];
     440             :                         
     441             :                         message = [messageQueue dequeue];
     442             :                         
     443             :                         if (!sSaturated && [message isKindOfClass:[NSData class]])
     444             :                         {
     445             :                                 size += [message length];
     446             :                                 if (size > 1 << 30)    // 1 GiB
     447             :                                 {
     448             :                                         sSaturated = YES;
     449             : #if OOLITE_WINDOWS
     450             :                                         message = @"\r\n\r\n\r\n***** LOG TRUNCATED DUE TO EXCESSIVE LENGTH *****\r\n";
     451             : #else
     452             :                                         message = @"\n\n\n***** LOG TRUNCATED DUE TO EXCESSIVE LENGTH *****\n";
     453             : #endif
     454             :                                         message = [message dataUsingEncoding:NSUTF8StringEncoding];
     455             :                                 }
     456             :                                 
     457             :                                 [logFile writeData:message];
     458             :                         }
     459             :                         else if ([message isEqual:@"flush"])
     460             :                         {
     461             :                                 [logFile synchronizeFile];
     462             :                         }
     463             :                         else if ([message isEqual:@"die"])
     464             :                         {
     465             :                                 break;
     466             :                         }
     467             :                         
     468             :                         [pool release];
     469             :                 }
     470             :         }
     471             :         @catch (NSException *exception) {}
     472             :         [pool release];
     473             :         
     474             :         // Clean up; after this, ivars are out of bounds.
     475             :         [messageQueue release];
     476             :         [threadStateMonitor lock];
     477             :         [threadStateMonitor unlockWithCondition:kConditionReadyToDealloc];
     478             :         
     479             :         [rootPool release];
     480             : }
     481             : 
     482             : @end
     483             : 
     484             : 
     485             : #if OOLITE_MAC_OS_X
     486             : 
     487             : /*      LoadLogCStringFunctions()
     488             :         
     489             :         We wish to make NSLogv() call our custom function OONSLogCStringFunction()
     490             :         rather than printing to stdout, by calling _NSSetLogCStringFunction().
     491             :         Additionally, in order to close the logger cleanly, we wish to be able to
     492             :         restore the standard logger, which requires us to call
     493             :         _NSLogCStringFunction(). These functions are private.
     494             :         _NSLogCStringFunction() is undocumented. _NSSetLogCStringFunction() is
     495             :         documented at http://docs.info.apple.com/article.html?artnum=70081 ,
     496             :         with the warning:
     497             :         
     498             :                 Be aware that this code references private APIs; this is an
     499             :                 unsupported workaround and users should use these instructions at
     500             :                 their own risk. Apple will not guarantee or provide support for
     501             :                 this procedure.
     502             :         
     503             :         The approach taken here is to load the functions dynamically. This makes
     504             :         us safe in the case of Apple removing the functions. In the unlikely event
     505             :         that they change the functions' paramters without renaming them, we would
     506             :         have a problem.
     507             : */
     508           0 : static void LoadLogCStringFunctions(void)
     509             : {
     510             :         CFBundleRef                                             foundationBundle = NULL;
     511             :         LogCStringFunctionGetterProc    getter = NULL;
     512             :         LogCStringFunctionSetterProc    setter = NULL;
     513             :         
     514             :         foundationBundle = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.Foundation"));
     515             :         if (foundationBundle != NULL)
     516             :         {
     517             :                 getter = CFBundleGetFunctionPointerForName(foundationBundle, CFSTR("_NSLogCStringFunction"));
     518             :                 setter = CFBundleGetFunctionPointerForName(foundationBundle, CFSTR("_NSSetLogCStringFunction"));
     519             :                 
     520             :                 if (getter != NULL && setter != NULL)
     521             :                 {
     522             :                         _NSLogCStringFunction = getter;
     523             :                         _NSSetLogCStringFunction = setter;
     524             :                 }
     525             :         }
     526             : }
     527             : 
     528             : 
     529           0 : static void OONSLogCStringFunction(const char *string, unsigned length, BOOL withSyslogBanner)
     530             : {
     531             :         if (OOLogWillDisplayMessagesInClass(@"system"))
     532             :         {
     533             :                 OOLogWithFunctionFileAndLine(@"system", NULL, NULL, 0, @"%s", string);
     534             :         }
     535             : }
     536             : 
     537             : #elif OOLITE_GNUSTEP
     538             : 
     539             : static void OONSLogPrintfHandler(NSString *message)
     540             : {
     541             :         if (OOLogWillDisplayMessagesInClass(@"gnustep"))
     542             :         {
     543             :                 OOLogWithFunctionFileAndLine(@"gnustep", NULL, NULL, 0, @"%@", message);
     544             :         }
     545             : }
     546             : 
     547             : #endif
     548             : 
     549             : 
     550           0 : static BOOL DirectoryExistCreatingIfNecessary(NSString *path)
     551             : {
     552             :         BOOL                            exists, directory;
     553             :         NSFileManager           *fmgr =  [NSFileManager defaultManager];
     554             :         
     555             :         exists = [fmgr fileExistsAtPath:path isDirectory:&directory];
     556             :         
     557             :         if (exists && !directory)
     558             :         {
     559             :                 NSLog(@"Log setup: expected %@ to be a folder, but it is a file.", path);
     560             :                 return NO;
     561             :         }
     562             :         if (!exists)
     563             :         {
     564             :                 if (![fmgr oo_createDirectoryAtPath:path attributes:nil])
     565             :                 {
     566             :                         NSLog(@"Log setup: could not create folder %@.", path);
     567             :                         return NO;
     568             :                 }
     569             :         }
     570             :         
     571             :         return YES;
     572             : }
     573             : 
     574             : 
     575             : #if OOLITE_MAC_OS_X
     576           0 : static void ExcludeFromTimeMachine(NSString *path)
     577             : {
     578             :         OSStatus (*CSBackupSetItemExcluded)(NSURL *item, Boolean exclude, Boolean excludeByPath) = NULL;
     579             :         CFBundleRef carbonCoreBundle = CFBundleGetBundleWithIdentifier(CFSTR("com.apple.CoreServices.CarbonCore"));
     580             :         if (carbonCoreBundle)
     581             :         {
     582             :                 CSBackupSetItemExcluded = CFBundleGetFunctionPointerForName(carbonCoreBundle, CFSTR("CSBackupSetItemExcluded"));
     583             :                 if (CSBackupSetItemExcluded != NULL)
     584             :                 {
     585             :                         (void)CSBackupSetItemExcluded([NSURL fileURLWithPath:path], YES, NO);
     586             :                 }
     587             :         }
     588             : }
     589             : 
     590           0 : static NSString *GetAppName(void)
     591             : {
     592             :         static NSString         *appName = nil;
     593             :         NSBundle                        *bundle = nil;
     594             : 
     595             :         if (appName == nil)
     596             :         {
     597             :                 bundle = [NSBundle mainBundle];
     598             :                 appName = [bundle objectForInfoDictionaryKey:@"CFBundleName"];
     599             :                 if (appName == nil)  appName = [bundle bundleIdentifier];
     600             :                 if (appName == nil)  appName = @"<unknown application>";
     601             :                 [appName retain];
     602             :         }
     603             : 
     604             :         return appName;
     605             : }
     606             : #endif
     607             : 
     608           0 : NSString *OOLogHandlerGetLogBasePath(void)
     609             : {
     610             :         static NSString         *basePath = nil;
     611             : 
     612             :         if (basePath == nil)
     613             :         {
     614             :                 const char *logdirEnv = SDL_getenv("OO_LOGSDIR");
     615             : 
     616             :                 if (logdirEnv)
     617             :                 {
     618             :                         basePath = [NSString stringWithUTF8String:logdirEnv];
     619             :                 }
     620             :                 else
     621             :                 {
     622             : #if OOLITE_MAC_OS_X
     623             :                         // ~/Library
     624             :                         basePath = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) objectAtIndex:0];
     625             : #elif OOLITE_LINUX
     626             :                         // ~
     627             :                         basePath = NSHomeDirectory();
     628             : 
     629             :                         // ~/.Oolite
     630             :                         basePath = [basePath stringByAppendingPathComponent:@".Oolite"];
     631             :                         if (!DirectoryExistCreatingIfNecessary(basePath))  return nil;
     632             : #elif OOLITE_WINDOWS
     633             :                         // <Install path>\Oolite
     634             :                         basePath = NSHomeDirectory();
     635             : #endif
     636             : 
     637             :                         // .../Logs
     638             :                         basePath = [basePath stringByAppendingPathComponent:@"Logs"];
     639             :                         if (!DirectoryExistCreatingIfNecessary(basePath))  return nil;
     640             : 
     641             : #if OOLITE_MAC_OS_X
     642             :                         // ~/Library/Logs/Oolite
     643             :                         basePath = [basePath stringByAppendingPathComponent:GetAppName()];
     644             :                         if (!DirectoryExistCreatingIfNecessary(basePath))  return nil;
     645             : #endif
     646             :                 }
     647             : #if OOLITE_MAC_OS_X
     648             :                 ExcludeFromTimeMachine(basePath);
     649             : #endif
     650             :                 [basePath retain];
     651             :         }
     652             : 
     653             :         return basePath;
     654             : }
     655             : 
     656             : #if SET_CRASH_REPORTER_INFO
     657             : 
     658           0 : static char **sCrashReporterInfo = NULL;
     659           0 : static char *sOldCrashReporterInfo = NULL;
     660           0 : static NSLock *sCrashReporterInfoLock = nil;
     661             : 
     662             : // Evil hackery based on http://www.allocinit.net/blog/2008/01/04/application-specific-information-in-leopard-crash-reports/
     663           0 : static void InitCrashReporterInfo(void)
     664             : {
     665             :         sCrashReporterInfo = dlsym(RTLD_DEFAULT, "__crashreporter_info__");
     666             :         if (sCrashReporterInfo != NULL)
     667             :         {
     668             :                 sCrashReporterInfoLock = [[NSLock alloc] init];
     669             :                 if (sCrashReporterInfoLock != nil)
     670             :                 {
     671             :                         sCrashReporterInfoAvailable = YES;
     672             :                 }
     673             :                 else
     674             :                 {
     675             :                         sCrashReporterInfo = NULL;
     676             :                 }
     677             :         }
     678             : }
     679             : 
     680           0 : static void SetCrashReporterInfo(const char *info)
     681             : {
     682             :         char                                    *copy = NULL, *old = NULL;
     683             :         
     684             :         /*      Don't do anything if setup failed or the string is NULL or empty.
     685             :                 (The NULL and empty checks may not be desirable in other uses.)
     686             :         */
     687             :         if (!sCrashReporterInfoAvailable || info == NULL || *info == '\0')  return;
     688             :         
     689             :         // Copy the string, which we assume to be dynamic...
     690             :         copy = strdup(info);
     691             :         if (copy == NULL)  return;
     692             :         
     693             :         /*      ...and swap it in.
     694             :                 Note that we keep a separate pointer to the old value, in case
     695             :                 something else overwrites __crashreporter_info__.
     696             :         */
     697             :         [sCrashReporterInfoLock lock];
     698             :         *sCrashReporterInfo = copy;
     699             :         old = sOldCrashReporterInfo;
     700             :         sOldCrashReporterInfo = copy;
     701             :         [sCrashReporterInfoLock unlock];
     702             :         
     703             :         // Delete our old string.
     704             :         if (old != NULL)  free(old);
     705             : }
     706             : 
     707             : #endif

Generated by: LCOV version 1.14