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
24 - (id)initWithDelegate
:(NSObject
<ProtocolClientDelegate
>*)delegate
{
25 if ((self = [super init
])) {
27 _delegateThread
= [NSThread currentThread
];
28 _lock
= [[NSRecursiveLock alloc
] init
];
39 return [_messageQueue isConnected
];
42 - (void)connectOnPort
:(NSUInteger
)port
{
43 assert(!_messageQueue
);
44 _messageQueue
= [[MessageQueue alloc
] initWithPort
:port delegate
:self];
45 [_messageQueue connect
];
49 [_messageQueue disconnect
];
52 - (NSNumber
*)sendCommandWithFormat
:(NSString
*)format
, ...
{
53 // Collect varargs and format command.
55 va_start(args
, format
);
56 NSString
* command
= [[NSString alloc
] initWithFormat
:format arguments
:args
];
59 NSNumber
* callbackKey
= [NSNumber numberWithInt
:_nextID
++];
60 NSString
* taggedCommand
= [NSString stringWithFormat
:@
"%@ -i %@", [command autorelease
], callbackKey
];
62 assert(_messageQueue
);
63 [_messageQueue sendMessage
:taggedCommand
];
67 - (NSNumber
*)sendCustomCommandWithFormat
:(NSString
*)format
, ...
{
68 // Collect varargs and format command.
70 va_start(args
, format
);
71 NSString
* command
= [[[NSString alloc
] initWithFormat
:format arguments
:args
] autorelease
];
74 NSNumber
* callbackKey
= [NSNumber numberWithInt
:_nextID
++];
75 NSString
* taggedCommand
= [command stringByReplacingOccurrencesOfString
:@
"{txn}"
76 withString
:[callbackKey stringValue
]];
78 [_messageQueue sendMessage
:taggedCommand
];
82 - (NSInteger
)transactionIDFromResponse
:(NSXMLDocument
*)response
{
83 return [[[[response rootElement
] attributeForName
:@
"transaction_id"] stringValue
] intValue
];
86 - (NSInteger
)transactionIDFromCommand
:(NSString
*)command
{
87 NSRange occurrence
= [command rangeOfString
:@
"-i "];
88 if (occurrence.location
== NSNotFound
)
90 NSString
* transaction
= [command substringFromIndex
:occurrence.location
+ occurrence.length
];
91 return [transaction intValue
];
94 + (NSString
*)escapedFilePathURI
:(NSString
*)path
{
95 // Custon GDBp paths are fine.
96 if ([[path substringToIndex
:4] isEqualToString
:@
"gdbp"])
99 // Create a temporary URL that will escape all the nasty characters.
100 NSURL
* url
= [NSURL fileURLWithPath
:path
];
101 NSString
* urlString
= [url absoluteString
];
103 // Remove the host because this is a file:// URL;
104 NSString
* host
= [url host
];
106 urlString
= [urlString stringByReplacingOccurrencesOfString
:[url host
] withString
:@
""];
108 // Escape % for use in printf-style NSString formatters.
109 urlString
= [urlString stringByReplacingOccurrencesOfString
:@
"%" withString
:@
"%%"];
113 // MessageQueueDelegate ////////////////////////////////////////////////////////
115 - (void)messageQueue
:(MessageQueue
*)queue error
:(NSError
*)error
{
116 NSLog(@
"error = %@", error
);
119 - (void)messageQueueDidConnect
:(MessageQueue
*)queue
{
126 [_delegate debuggerEngineConnected
:self];
129 - (void)messageQueueDidDisconnect
:(MessageQueue
*)queue
{
130 [_messageQueue release
];
132 [_delegate debuggerEngineDisconnected
:self];
135 // Callback for when a message has been sent.
136 - (void)messageQueue
:(MessageQueue
*)queue didSendMessage
:(NSString
*)message
138 NSInteger tag
= [self transactionIDFromCommand
:message
];
140 _lastWrittenID
= tag
;
143 LoggingController
* logger
= [[AppDelegate instance
] loggingController
];
144 LogEntry
* entry
= [LogEntry newSendEntry
:message
];
145 entry.lastReadTransactionID
= _lastReadID
;
146 entry.lastWrittenTransactionID
= _lastWrittenID
;
147 [logger recordEntry
:entry
];
150 // Callback with the message content when one has been receieved.
151 - (void)messageQueue
:(MessageQueue
*)queue didReceiveMessage
:(NSString
*)message
153 LoggingController
* logger
= [[AppDelegate instance
] loggingController
];
154 LogEntry
* entry
= [LogEntry newReceiveEntry
:message
];
155 entry.lastReadTransactionID
= _lastReadID
;
156 entry.lastWrittenTransactionID
= _lastWrittenID
;
157 [logger recordEntry
:entry
];
159 // Test if we can convert it into an NSXMLDocument.
160 NSError
* error
= nil;
161 NSXMLDocument
* xml
= [[NSXMLDocument alloc
] initWithXMLString
:message
162 options
:NSXMLDocumentTidyXML
165 [self messageQueue
:queue error
:error
];
169 // Validate the transaction.
170 NSInteger transaction
= [self transactionIDFromResponse
:xml
];
171 if (transaction
< _lastReadID
) {
172 NSLog(@
"Transaction #%d is out of date (lastRead = %d). Dropping packet: %@",
173 transaction
, _lastReadID
, message
);
176 if (transaction
!= _lastWrittenID
) {
177 NSLog(@
"Transaction #%d received out of order. lastRead = %d, lastWritten = %d. Continuing.",
178 transaction
, _lastReadID
, _lastWrittenID
);
181 _lastReadID
= transaction
;
182 entry.lastReadTransactionID
= _lastReadID
;
184 [_delegate debuggerEngine
:self receivedMessage
:xml
];