apps-projectcenter/Modules/Debuggers/ProjectCenter/PipeDelegate.m

729 lines
17 KiB
Objective-C

/*
** PipeDelegate.m
**
** Copyright (c) 2008-2020 Free Software Foundation
**
** Author: Gregory Casamento <greg.casamento@gmail.com>
** Riccardo Mottola <rm@gnu.org>
**
** 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
*/
#include <sys/stat.h>
#include <signal.h>
#include <stdio.h> /* for stderr and perror*/
#include <errno.h> /* for int errno */
#include <fcntl.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#import "PipeDelegate.h"
#import "PCDebugger.h"
#ifndef NOTIFICATION_CENTER
#define NOTIFICATION_CENTER [NSNotificationCenter defaultCenter]
#endif
@implementation PipeDelegate
- (id)init
{
if ((self = [super init]))
{
userInputColor = [[NSColor blueColor] retain];
debuggerColor = [[NSColor blackColor] retain];
messageColor = [[NSColor brownColor] retain];
errorColor = [[NSColor redColor] retain];
promptColor = [[NSColor purpleColor] retain];
debuggerStarted = NO;
}
return self;
}
- (NSTextView *)textView
{
return tView;
}
- (void)setTextView: (NSTextView *)tv
{
if (tView != tv)
{
[tView release];
tView = tv;
[tView retain];
}
}
- (PCDebugger *)debugger
{
return debugger;
}
- (void)setDebugger:(PCDebugger *)dbg
{
if (debugger != dbg)
{
[debugger release];
debugger = dbg;
[debugger retain];
}
}
- (void)setFont:(NSFont *)aFont
{
if (font != aFont)
{
[font release];
font = aFont;
[font retain];
}
}
- (NSColor *)userInputColor
{
return userInputColor;
}
- (NSColor *)debuggerColor
{
return debuggerColor;
}
- (NSColor *)messageColor
{
return messageColor;
}
- (NSColor *)errorColor
{
return errorColor;
}
/**
* Log string to the view.
*/
- (void) logString:(NSString *)str
newLine:(BOOL)newLine
withColor:(NSColor *)color
{
NSMutableDictionary *textAttributes;
NSAttributedString *attrStr;
if (newLine)
{
str = [str stringByAppendingString:@"\n"];
}
textAttributes = [NSMutableDictionary dictionary];
[textAttributes setObject:font forKey:NSFontAttributeName];
if (color)
{
[textAttributes setObject:color forKey:NSForegroundColorAttributeName];
}
attrStr = [[NSAttributedString alloc] initWithString: str
attributes: textAttributes];
[[tView textStorage] appendAttributedString: attrStr];
[attrStr release];
[tView scrollRangeToVisible:NSMakeRange([[tView string] length], 0)];
[tView setNeedsDisplay:YES];
}
- (PCDebuggerOutputTypes) parseStringLine: (NSString *)stringInput
{
NSScanner *stringScanner;
NSString *prefix = NULL;
if ([stringInput length] == 0)
return PCDBEmptyRecord;
stringScanner = [NSScanner scannerWithString: stringInput];
//NSLog(@"parsing: |%@|", stringInput);
[stringScanner scanString: @"(gdb)" intoString: &prefix];
if(prefix != nil)
{
if(debuggerStarted == NO)
{
[NOTIFICATION_CENTER postNotificationName:PCDBDebuggerStartedNotification
object: nil];
debuggerStarted = YES;
}
return PCDBPromptRecord;
}
[stringScanner scanString: @"=" intoString: &prefix];
if(prefix != nil)
{
NSString *dictionaryName = NULL;
[stringScanner scanUpToString: @"," intoString: &dictionaryName];
if([dictionaryName isEqualToString: @"thread-group-started"])
{
NSLog(@"%@",dictionaryName);
}
if(dictionaryName != nil)
{
NSString *key = NULL;
NSString *value = NULL;
while([stringScanner isAtEnd] == NO)
{
[stringScanner scanString: @"," intoString: NULL];
[stringScanner scanUpToString: @"=" intoString: &key];
[stringScanner scanString: @"=" intoString: NULL];
[stringScanner scanString: @"\"" intoString: NULL];
[stringScanner scanUpToString: @"\"" intoString: &value];
[stringScanner scanString: @"\"" intoString: NULL];
if([key isEqualToString:@"pid"] &&
[dictionaryName isEqualToString: @"thread-group-started"])
{
[debugger setSubProcessId: [value intValue]];
}
}
}
return PCDBAsyncInfoRecord;
}
[stringScanner scanString: @"*" intoString: &prefix];
if(prefix != nil)
{
return PCDBAsyncStatusRecord;
}
[stringScanner scanString: @"<-" intoString: &prefix];
if(prefix != nil)
{
return PCDBBreakpointRecord;
}
[stringScanner scanString: @"->" intoString: &prefix];
if(prefix != nil)
{
return PCDBBreakpointRecord;
}
[stringScanner scanString: @"~" intoString: &prefix];
if(prefix != nil)
{
if ([debugger gdbVersion] == 0.0)
{
NSString *str1 = nil;
NSString *str2 = nil;
[stringScanner scanString: @"\"GNU gdb" intoString: &str1];
if (str1 != nil)
{
[stringScanner scanString: @" (GDB)" intoString: &str2];
}
if (str2 != nil || str1 != nil)
{
float v;
if ([stringScanner scanFloat:&v])
{
NSLog(@"GDB version string: %f", v);
[debugger setGdbVersion:v];
}
}
}
if (([debugger gdbVersion] < 7) && [debugger subProcessId] == 0)
{
NSString *str1;
// we attempt to parse: [New thread 6800.0x18ec]
[stringScanner scanString: @"\"[New thread" intoString: &str1];
if (str1 != nil)
{
int v;
if([stringScanner scanInt:&v])
{
NSLog(@"sub process id: %d", v);
[debugger setSubProcessId:v];
}
}
}
return PCDBConsoleStreamRecord;
}
[stringScanner scanString: @"@" intoString: &prefix];
if(prefix != nil)
{
return PCDBTargetStreamRecord;
}
[stringScanner scanString: @"&" intoString: &prefix];
if(prefix != nil)
{
return PCDBDebugStreamRecord;
}
[stringScanner scanString: @"^" intoString: &prefix];
if(prefix != nil)
{
NSString *result = nil;
[stringScanner scanString: @"done" intoString: &result];
if(result != nil)
{
[debugger setStatus: @"Done"];
return PCDBResultRecord;
}
[stringScanner scanString: @"running" intoString: &result];
if(result != nil)
{
[debugger setStatus: @"Running"];
return PCDBResultRecord;
}
[stringScanner scanString: @"connected" intoString: &result];
if(result != nil)
{
[debugger setStatus: @"Connected"];
return PCDBResultRecord;
}
[stringScanner scanString: @"error" intoString: &result];
if(result != nil)
{
[debugger setStatus: @"Error"];
return PCDBResultRecord;
}
[stringScanner scanString: @"exit" intoString: &result];
if(result != nil)
{
[debugger setStatus: @"Exit"];
return PCDBResultRecord;
}
return PCDBResultRecord;
}
NSLog(@"No match found parse: |%@|", stringInput);
return PCDBNotFoundRecord;
}
- (NSString *)unescapeOutputRecord: (NSString *)recordString
{
NSString *unescapedString = [recordString copy];
if ([unescapedString hasPrefix:@"~\""])
unescapedString = [unescapedString substringFromIndex:2];
if ([unescapedString hasSuffix:@"\""])\
unescapedString = [unescapedString substringToIndex: [unescapedString length] - 1];
unescapedString = [unescapedString stringByReplacingOccurrencesOfString: @"\\\"" withString: @"\""];
unescapedString = [unescapedString stringByReplacingOccurrencesOfString: @"\\n" withString: @"\n"];
unescapedString = [unescapedString stringByReplacingOccurrencesOfString: @"\\t" withString: @"\t"];
unescapedString = [unescapedString stringByReplacingOccurrencesOfString: @"\\032" withString: @" "];
return unescapedString;
}
- (void) parseString: (NSString *)inputString
{
NSArray *components;
NSEnumerator *en;
NSString *item = nil;
#if defined(__MINGW32__)
components = [inputString componentsSeparatedByString:@"\r\n"];
#else
components = [inputString componentsSeparatedByString:@"\n"];
#endif
en = [components objectEnumerator];
while((item = [en nextObject]) != nil)
{
PCDebuggerOutputTypes outtype = [self parseStringLine: item];
if(outtype == PCDBConsoleStreamRecord ||
outtype == PCDBTargetStreamRecord)
{
NSString *unescapedString = [self unescapeOutputRecord: item];
[self logString: unescapedString newLine: NO withColor:debuggerColor];
}
else if(outtype == PCDBPromptRecord)
{
[self logString: item newLine: NO withColor:promptColor];
}
/*
else if(outtype == PCDBNotFoundRecord)
{
[self logString: item newLine: NO withColor:promptColor];
}
*/
}
/*
stringRange = [inputString rangeOfString: "(gdb)" options: NULL];
if(stringRange.location == NSNotFound)
{
[self logString: inputString newLine: NO withColor:debuggerColor];
}
else
{
}
*/
}
/**
* Log standard out.
*/
- (void) logStdOut:(NSNotification *)aNotif
{
NSData *data;
NSFileHandle *handle = stdoutHandle;
if ((data = [handle availableData]) && [data length] > 0)
{
NSString *dataString;
dataString = [[NSString alloc]
initWithData:data
encoding:[NSString defaultCStringEncoding]];
// if( !
[self parseString: dataString]; // )
// {
// [self logString: dataString newLine: NO withColor:debuggerColor];
// }
RELEASE(dataString);
}
if (task)
{
[handle waitForDataInBackgroundAndNotify];
}
else
{
[NOTIFICATION_CENTER removeObserver: self
name: NSFileHandleDataAvailableNotification
object: handle];
}
}
/**
* Log error out.
*/
- (void) logErrOut:(NSNotification *)aNotif
{
NSData *data;
NSFileHandle *handle = error_handle;
if ((data = [handle availableData]) && [data length] > 0)
{
NSString *dataString;
dataString = [[NSString alloc]
initWithData:data
encoding:[NSString defaultCStringEncoding]];
// if(![self parseString: dataString])
{
[self logString: dataString newLine: NO withColor:errorColor];
}
RELEASE(dataString);
}
if (task)
{
[handle waitForDataInBackgroundAndNotify];
}
else
{
[NOTIFICATION_CENTER removeObserver:self
name: NSFileHandleDataAvailableNotification
object: handle];
}
}
/**
* Notified when the task is completed.
*/
- (void) taskDidTerminate: (NSNotification *)notif
{
NSLog(@"Task Terminated...");
[self logString: [self stopMessage]
newLine:YES
withColor:messageColor];
}
/**
* Message to print when the task starts
*/
- (NSString *) startMessage
{
return @"=== Task Started ===";
}
/**
* Message to print when the task stops
*/
- (NSString *) stopMessage
{
return @"\n=== Task Stopped ===";
}
/**
* Start the program.
*/
- (void) runProgram: (NSString *)path
inCurrentDirectory: (NSString *)directory
withArguments: (NSArray *)array
logStandardError: (BOOL)logError
{
NSPipe *inPipe;
NSPipe *outPipe;
task = [[NSTask alloc] init];
[task setArguments: array];
[task setCurrentDirectoryPath: directory];
[task setLaunchPath: path];
inPipe = [NSPipe pipe];
outPipe = [NSPipe pipe];
stdinHandle = [[inPipe fileHandleForWriting] retain];
stdoutHandle = [[outPipe fileHandleForReading] retain];
[task setStandardOutput: outPipe];
[task setStandardInput: inPipe];
[stdoutHandle waitForDataInBackgroundAndNotify];
// Log standard error, if requested.
if(logError)
{
[task setStandardError: [NSPipe pipe]];
error_handle = [[task standardError] fileHandleForReading];
[error_handle waitForDataInBackgroundAndNotify];
[NOTIFICATION_CENTER addObserver:self
selector:@selector(logErrOut:)
name:NSFileHandleDataAvailableNotification
object:error_handle];
}
// set up notifications to get data.
[NOTIFICATION_CENTER addObserver:self
selector:@selector(logStdOut:)
name:NSFileHandleDataAvailableNotification
object:stdoutHandle];
[NOTIFICATION_CENTER addObserver:self
selector:@selector(taskDidTerminate:)
name:NSTaskDidTerminateNotification
object:task];
// run the task...
NS_DURING
{
[self logString: [self startMessage]
newLine:YES
withColor:messageColor];
[task launch];
}
NS_HANDLER
{
NSRunAlertPanel(@"Problem Launching Debugger",
[localException reason],
@"OK", nil, nil, nil);
NSLog(@"Task Terminated Unexpectedly...");
[self logString: @"\n=== Task Terminated Unexpectedly ===\n"
newLine:NO
withColor:messageColor];
//Clean up after task is terminated
[[NSNotificationCenter defaultCenter]
postNotificationName: NSTaskDidTerminateNotification
object: task];
}
NS_ENDHANDLER
}
- (void) terminate
{
if(task)
{
[task terminate];
}
}
- (void) dealloc
{
[NOTIFICATION_CENTER removeObserver: self];
[self terminate];
[userInputColor release];
[debuggerColor release];
[messageColor release];
[errorColor release];
[tView release];
[super dealloc];
}
- (void) putString: (NSString *)string;
{
unichar *str = (unichar *)[string cStringUsingEncoding: [NSString defaultCStringEncoding]];
int len = strlen((char *)str);
NSData *data = [NSData dataWithBytes: str length: len];
[stdinHandle writeData: data];
[stdinHandle synchronizeFile];
}
/* for input as typed from the user */
- (void) typeString: (NSString *)string
{
NSUInteger strLen;
strLen = [string length];
[self putString:string];
// if we have a single backspace or delete character
if (strLen == 1 && [string characterAtIndex:strLen-1] == '\177')
{
NSUInteger textLen;
textLen = [[tView string] length];
[tView setSelectedRange:NSMakeRange(textLen-1, 1)];
[tView delete:nil];
return;
}
[self logString:string newLine:NO withColor:userInputColor];
}
/**
* Put a single character into the stream.
*/
- (void) putChar:(unichar)ch
{
NSData *data = [NSData dataWithBytes: &ch length: 1];
[stdinHandle writeData: data];
}
- (void) interrupt
{
[task interrupt];
}
/**
* Respond to key events and pipe them down to the debugger
*/
- (void) keyDown: (NSEvent*)theEvent
{
NSString *chars;
chars = [theEvent characters];
if ([chars length] == 0)
{
}
else if ([chars length] == 1)
{
unichar c;
c = [chars characterAtIndex: 0];
//NSLog(@"char: %d", c);
if (c == 3) // ETX, Control-C
{
[self interrupt]; // send the interrupt signal to the subtask
}
else if (c == 13) // CR
{
[self typeString: @"\n"];
}
else if (c == 127) // del (usually backspace)
{
NSString *tss = [[tView textStorage] string];
if (![tss hasSuffix:@"\n"] && ![tss hasSuffix:@"(gdb) "])
{
[self typeString: chars];
}
}
else
{
[self typeString: chars];
}
}
else
NSLog(@"characters: |%@|", chars);
}
- (void) setBreakpoints:(NSArray *)breakpoints
{
NSDictionary *bp;
NSEnumerator *e;
// TODO
e = [breakpoints objectEnumerator];
while ((bp = [e nextObject]))
{
NSString *bpType;
NSString *bpString;
bpType = [bp objectForKey:PCBreakTypeKey];
bpString = nil;
if ([bpType isEqualToString:PCBreakTypeByLine])
{
NSString *fileName;
NSNumber *lineNumber;
fileName = [bp objectForKey:PCBreakFilename];
lineNumber = [bp objectForKey:PCBreakLineNumber];
bpString = [NSString stringWithFormat:@"-break-insert -f %@:%@\n", fileName, lineNumber];
}
else if ([bpType isEqualToString:PCBreakTypeMethod])
{
NSString *methodName;
methodName = [bp objectForKey:PCBreakMethod];
bpString = [NSString stringWithFormat:@"-interpreter-exec console \"break %@\"\n", methodName];
}
else
{
NSLog(@"Unknown breakpoint type: %@", bpType);
}
if (bpString)
{
NSString *command;
/* TODO: split into a separate insert function */
command = bpString;
NSLog(@"gdb mi command is: %@", command);
[self putString: command];
}
}
}
- (void) debuggerSetup
{
NSString *command = @"set confirm off\n";
[self putString: command];
}
@end