William Bowling is sharing code with you
Bitbucket is a code hosting site. Unlimited public and private repositories. Free for small teams.
Don't show this again1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 | /*
* Adium is the legal property of its developers, whose names are listed in the copyright file included
* with this source distribution.
*
* This program is free software; you can redistribute it and/or modify it under the terms of the GNU
* General Public License as published by the Free Software Foundation; either version 2 of the License,
* or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
* the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along with this program; if not,
* write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#import "ESDebugController.h"
#import "ESDebugWindowController.h"
#import <Adium/AIMenuControllerProtocol.h>
#import <AIUtilities/AIMenuAdditions.h>
#import <fcntl.h> //open(2)
#import <unistd.h> //close(2)
#import <errno.h> //errno
#import <string.h> //strerror(3)
#import <objc/objc-runtime.h>
#define CACHED_DEBUG_LOGS 100 //Number of logs to keep at any given time
#define KEY_DEBUG_WINDOW_OPEN @"Debug Window Open"
@interface ESDebugController()
- (void) start:(NSNotification *)dummy;
@end
@implementation ESDebugController
//Throwing an exception isn't enough, we need to die completely.
void AIExplodeOnEnumerationMutation(id dummy) {
NSLog(@"Attempted to mutate collection %@ of class %@ while enumerating", dummy, [dummy class]);
*((int*)0xdeadbeef) = 42;
}
- (id)init
{
if ((self = [super init])) {
#ifdef DEBUG_BUILD
objc_setEnumerationMutationHandler(AIExplodeOnEnumerationMutation);
#endif
debugLogArray = [[NSMutableArray alloc] init];
}
return self;
}
- (void)controllerDidLoad
{
if (AIDebugLoggingEnabled) {
[self start:nil];
} else {
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(start:) name:AIDebugLoggingEnabledNotification object:nil];
}
}
- (void) start:(NSNotification *)dummy {
//Contact list menu item
NSMenuItem *menuItem = [[NSMenuItem allocWithZone:[NSMenu menuZone]] initWithTitle:AILocalizedString(@"Debug Window",nil)
target:self
action:@selector(showDebugWindow:)
keyEquivalent:@""];
[adium.menuController addMenuItem:menuItem toLocation:LOC_Adium_About];
[menuItem release];
//Restore the debug window if it was open when we quit last time
if ([[adium.preferenceController preferenceForKey:KEY_DEBUG_WINDOW_OPEN
group:GROUP_DEBUG] boolValue]) {
[ESDebugWindowController showDebugWindow];
}
[adium.preferenceController registerPreferenceObserver:self forGroup:GROUP_DEBUG];
}
- (void)controllerWillClose
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
//Save the open state of the debug window
[adium.preferenceController setPreference:([ESDebugWindowController debugWindowIsOpen] ?
[NSNumber numberWithBool:YES] :
nil)
forKey:KEY_DEBUG_WINDOW_OPEN
group:GROUP_DEBUG];
[ESDebugWindowController closeDebugWindow];
}
- (void)dealloc
{
[debugLogArray release];
[debugLogFile closeFile];
[debugLogFile release];
[super dealloc];
}
- (void)showDebugWindow:(id)sender
{
[NSApp activateIgnoringOtherApps:YES];
[ESDebugWindowController showDebugWindow];
}
- (void)addMessage:(NSString *)actualMessage
{
if ((![actualMessage hasSuffix:@"\n"]) && (![actualMessage hasSuffix:@"\r"])) {
actualMessage = [actualMessage stringByAppendingString:@"\n"];
}
[debugLogArray addObject:actualMessage];
if (debugLogFile) {
[debugLogFile writeData:[actualMessage dataUsingEncoding:NSUTF8StringEncoding]];
}
//Keep debugLogArray to a reasonable size
if ([debugLogArray count] > CACHED_DEBUG_LOGS) [debugLogArray removeObjectAtIndex:0];
[ESDebugWindowController addedDebugMessage:actualMessage];
}
- (void)preferencesChangedForGroup:(NSString *)group key:(NSString *)key
object:(AIListObject *)object preferenceDict:(NSDictionary *)prefDict firstTime:(BOOL)firstTime
{
if (firstTime || [key isEqualToString:KEY_DEBUG_WRITE_LOG]) {
BOOL writeLogs = [[prefDict objectForKey:KEY_DEBUG_WRITE_LOG] boolValue];
if (writeLogs) {
[self debugLogFile];
} else {
[debugLogFile release]; debugLogFile = nil;
}
}
}
- (NSArray *)debugLogArray
{
return debugLogArray;
}
- (void)clearDebugLogArray
{
[debugLogArray removeAllObjects];
}
- (NSFileHandle *)debugLogFile
{
if (!debugLogFile) {
NSFileManager *mgr = [NSFileManager defaultManager];
NSCalendarDate *date = [NSCalendarDate calendarDate];
NSString *folder, *dateString, *filename, *pathname;
NSUInteger counter = 0;
NSInteger fd;
//make sure the containing folder for debug logs exists.
folder = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, /*expandTilde*/ YES) objectAtIndex:0];
folder = [folder stringByAppendingPathComponent:@"Logs"];
folder = [folder stringByAppendingPathComponent:@"Adium Debug"];
BOOL success = [mgr createDirectoryAtPath:folder withIntermediateDirectories:YES attributes:nil error:NULL];
if((!success) && (errno != EEXIST)) {
/*raise an exception if the folder could not be created,
* but not if that was because it already exists.
*/
NSAssert2(success, @"Could not create folder %@: %s", folder, strerror(errno));
}
/*get today's date, for the filename.
*the date is in YYYY-MM-DD format. duplicates are disambiguated with
*' 1', ' 2', ' 3', etc. appendages.
*/
filename = dateString = [date descriptionWithCalendarFormat:@"%Y-%m-%d"];
while([mgr fileExistsAtPath:(pathname = [folder stringByAppendingPathComponent:[filename stringByAppendingPathExtension:@"log"]])]) {
filename = [dateString stringByAppendingFormat:@" %lu", ++counter];
}
//create (if necessary) and open the file as writable, in append mode.
fd = open([pathname fileSystemRepresentation], O_CREAT | O_WRONLY | O_APPEND, 0644);
NSAssert2(fd > -1, @"could not create %@ nor open it for writing: %s", pathname, strerror(errno));
//note: the file handle takes ownership of fd.
/*
* From the docs: "The object creating an NSFileHandle using this method owns fileDescriptor and is responsible for its disposition."
* which seems to indicate that the file handle does not take ownership of fd. Just for the record. -eds
*/
debugLogFile = [[NSFileHandle alloc] initWithFileDescriptor:fd];
if(!debugLogFile) close(fd);
NSAssert1(debugLogFile != nil, @"could not create file handle for %@", pathname);
//write header (separates this session from previous sessions).
[debugLogFile writeData:[[NSString stringWithFormat:@"Opened debug log at %@\n", date] dataUsingEncoding:NSUTF8StringEncoding]];
}
return debugLogFile;
}
@end
|