3 * Copyright (c) 2013, Blue Static <http://www.bluestatic.org>
5 * This program is free software; you can redistribute it and/or modify it under the terms of the GNU
6 * General Public License as published by the Free Software Foundation; either version 2 of the
7 * License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
10 * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * General Public License for more details.
13 * You should have received a copy of the GNU General Public License along with this program; if not,
14 * write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
17 #import "ProtocolClient.h"
19 #import "AppDelegate.h"
20 #import "LoggingController.h"
22 @implementation ProtocolClient
{
23 // The object responsible for the actual communication with the debug server.
24 MessageQueue
* _messageQueue
;
26 // The delegate of this class, which receives high-level messages about the
27 // state of the debugger.
28 id<ProtocolClientDelegate
> _delegate
; // weak
30 // The next transaction ID to assign.
33 // Records the last read and written transaction IDs. These are only used in
34 // creating LogEntry objects.
35 NSInteger _lastReadID
;
36 NSInteger _lastWrittenID
;
39 - (id)initWithDelegate
:(id<ProtocolClientDelegate
>)delegate
{
40 if ((self = [super init
])) {
51 return [_messageQueue isConnected
];
54 - (void)connectOnPort
:(NSUInteger
)port
{
55 assert(!_messageQueue
);
56 _messageQueue
= [[MessageQueue alloc
] initWithPort
:port delegate
:self];
57 [_messageQueue connect
];
61 [_messageQueue disconnect
];
64 - (NSNumber
*)sendCommandWithFormat
:(NSString
*)format
, ...
{
65 // Collect varargs and format command.
67 va_start(args
, format
);
68 NSString
* command
= [[NSString alloc
] initWithFormat
:format arguments
:args
];
71 NSNumber
* callbackKey
= [NSNumber numberWithInt
:_nextID
++];
72 NSString
* taggedCommand
= [NSString stringWithFormat
:@
"%@ -i %@", [command autorelease
], callbackKey
];
74 assert(_messageQueue
);
75 [_messageQueue sendMessage
:taggedCommand
];
79 - (NSNumber
*)sendCustomCommandWithFormat
:(NSString
*)format
, ...
{
80 // Collect varargs and format command.
82 va_start(args
, format
);
83 NSString
* command
= [[[NSString alloc
] initWithFormat
:format arguments
:args
] autorelease
];
86 NSNumber
* callbackKey
= [NSNumber numberWithInt
:_nextID
++];
87 NSString
* taggedCommand
= [command stringByReplacingOccurrencesOfString
:@
"{txn}"
88 withString
:[callbackKey stringValue
]];
90 [_messageQueue sendMessage
:taggedCommand
];
94 - (NSInteger
)transactionIDFromResponse
:(NSXMLDocument
*)response
{
95 return [[[[response rootElement
] attributeForName
:@
"transaction_id"] stringValue
] intValue
];
98 - (NSInteger
)transactionIDFromCommand
:(NSString
*)command
{
99 NSRange occurrence
= [command rangeOfString
:@
"-i "];
100 if (occurrence.location
== NSNotFound
)
102 NSString
* transaction
= [command substringFromIndex
:occurrence.location
+ occurrence.length
];
103 return [transaction intValue
];
106 + (NSString
*)escapedFilePathURI
:(NSString
*)path
{
107 // Custon GDBp paths are fine.
108 if ([[path substringToIndex
:4] isEqualToString
:@
"gdbp"])
111 // Create a temporary URL that will escape all the nasty characters.
112 NSURL
* url
= [NSURL fileURLWithPath
:path
];
113 NSString
* urlString
= [url absoluteString
];
115 // Remove the host because this is a file:// URL;
116 NSString
* host
= [url host
];
118 urlString
= [urlString stringByReplacingOccurrencesOfString
:[url host
] withString
:@
""];
120 // Escape % for use in printf-style NSString formatters.
121 urlString
= [urlString stringByReplacingOccurrencesOfString
:@
"%" withString
:@
"%%"];
125 // MessageQueueDelegate ////////////////////////////////////////////////////////
127 - (void)messageQueue
:(MessageQueue
*)queue error
:(NSError
*)error
{
128 NSLog(@
"error = %@", error
);
131 - (void)messageQueueDidConnect
:(MessageQueue
*)queue
{
136 [_delegate debuggerEngineConnected
:self];
139 - (void)messageQueueDidDisconnect
:(MessageQueue
*)queue
{
140 [_messageQueue release
];
142 [_delegate debuggerEngineDisconnected
:self];
145 // Callback for when a message has been sent.
146 - (void)messageQueue
:(MessageQueue
*)queue didSendMessage
:(NSString
*)message
148 NSInteger tag
= [self transactionIDFromCommand
:message
];
149 _lastWrittenID
= tag
;
151 LoggingController
* logger
= [[AppDelegate instance
] loggingController
];
152 LogEntry
* entry
= [LogEntry newSendEntry
:message
];
153 entry.lastReadTransactionID
= _lastReadID
;
154 entry.lastWrittenTransactionID
= _lastWrittenID
;
155 [logger recordEntry
:entry
];
158 // Callback with the message content when one has been receieved.
159 - (void)messageQueue
:(MessageQueue
*)queue didReceiveMessage
:(NSString
*)message
161 LoggingController
* logger
= [[AppDelegate instance
] loggingController
];
162 LogEntry
* entry
= [LogEntry newReceiveEntry
:message
];
163 entry.lastReadTransactionID
= _lastReadID
;
164 entry.lastWrittenTransactionID
= _lastWrittenID
;
165 [logger recordEntry
:entry
];
167 // Test if we can convert it into an NSXMLDocument.
168 NSError
* error
= nil;
169 NSXMLDocument
* xml
= [[NSXMLDocument alloc
] initWithXMLString
:message
170 options
:NSXMLDocumentTidyXML
173 [self messageQueue
:queue error
:error
];
177 _lastReadID
= [self transactionIDFromResponse
:xml
];
178 entry.lastReadTransactionID
= _lastReadID
;
180 [_delegate debuggerEngine
:self receivedMessage
:xml
];