Warning: That file was not part of the compilation database. It may have many parsing errors.
1 | /**************************************************************************** |
---|---|
2 | ** |
3 | ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). |
4 | ** Contact: http://www.qt-project.org/legal |
5 | ** |
6 | ** This file is part of the tools applications of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and Digia. For licensing terms and |
14 | ** conditions see http://qt.digia.com/licensing. For further information |
15 | ** use the contact form at http://qt.digia.com/contact-us. |
16 | ** |
17 | ** GNU Lesser General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
19 | ** General Public License version 2.1 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPL included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 2.1 requirements |
23 | ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
24 | ** |
25 | ** In addition, as a special exception, Digia gives you certain additional |
26 | ** rights. These rights are described in the Digia Qt LGPL Exception |
27 | ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
28 | ** |
29 | ** GNU General Public License Usage |
30 | ** Alternatively, this file may be used under the terms of the GNU |
31 | ** General Public License version 3.0 as published by the Free Software |
32 | ** Foundation and appearing in the file LICENSE.GPL included in the |
33 | ** packaging of this file. Please review the following information to |
34 | ** ensure the GNU General Public License version 3.0 requirements will be |
35 | ** met: http://www.gnu.org/copyleft/gpl.html. |
36 | ** |
37 | ** |
38 | ** $QT_END_LICENSE$ |
39 | ** |
40 | ****************************************************************************/ |
41 | |
42 | #include <QCoreApplication> |
43 | #include <QDebug> |
44 | #include <QDir> |
45 | #include <QFile> |
46 | #include <QObject> |
47 | #include <QTimer> |
48 | #include "codasignalhandler.h" |
49 | #include "texttracehandler.h" |
50 | |
51 | static const quint64 DEFAULT_CHUNK_SIZE = 40000; |
52 | |
53 | class CodaSignalHandlerPrivate |
54 | { |
55 | friend class CodaSignalHandler; |
56 | public: |
57 | CodaSignalHandlerPrivate(); |
58 | ~CodaSignalHandlerPrivate(); |
59 | private: |
60 | SymbianUtils::CodaDevicePtr codaDevice; |
61 | QEventLoop *eventLoop; |
62 | QTextStream out; |
63 | QTextStream err; |
64 | int loglevel; |
65 | int timeout; |
66 | int result; |
67 | CodaAction action; |
68 | QString copySrcFileName; |
69 | QString copyDstFileName; |
70 | QString dlSrcFileName; |
71 | QString dlDstFileName; |
72 | QString appFileName; |
73 | QString commandLineArgs; |
74 | QString serialPortName; |
75 | QString appID; |
76 | QByteArray remoteFileHandle; |
77 | QScopedPointer<QFile> localFile; |
78 | QScopedPointer<QFile> remoteFile; |
79 | quint64 putChunkSize; |
80 | quint64 putLastChunkSize; |
81 | quint64 remoteBytesLeft; |
82 | quint64 remoteFileSize; |
83 | bool putWriteOk; |
84 | bool connected; |
85 | bool debugSessionControl; |
86 | }; |
87 | |
88 | CodaSignalHandlerPrivate::CodaSignalHandlerPrivate() |
89 | : eventLoop(0), |
90 | out(stdout), |
91 | err(stderr), |
92 | loglevel(0), |
93 | timeout(0), |
94 | result(0), |
95 | action(ActionPingOnly), |
96 | putChunkSize(DEFAULT_CHUNK_SIZE), |
97 | putLastChunkSize(0), |
98 | remoteBytesLeft(0), |
99 | remoteFileSize(0), |
100 | putWriteOk(false), |
101 | connected(false), |
102 | debugSessionControl(false) |
103 | { |
104 | } |
105 | |
106 | CodaSignalHandlerPrivate::~CodaSignalHandlerPrivate() |
107 | { |
108 | delete eventLoop; |
109 | out.flush(); |
110 | err.flush(); |
111 | } |
112 | |
113 | void CodaSignalHandler::error(const QString &errorMessage) |
114 | { |
115 | reportError(tr("CODA error: %1").arg(errorMessage)); |
116 | } |
117 | |
118 | void CodaSignalHandler::logMessage(const QString &logMessage) |
119 | { |
120 | reportMessage(tr("CODA log: %1").arg(logMessage)); |
121 | } |
122 | |
123 | void CodaSignalHandler::serialPong(const QString &codaVersion) |
124 | { |
125 | reportMessage(tr("CODA version: %1").arg(codaVersion)); |
126 | } |
127 | |
128 | void CodaSignalHandler::tcfEvent(const Coda::CodaEvent &event) |
129 | { |
130 | reportMessage(tr("CODA event: Type: %1 Message: %2").arg(event.type()).arg(event.toString())); |
131 | |
132 | switch (event.type()) { |
133 | case Coda::CodaEvent::LocatorHello: |
134 | handleConnected(event); |
135 | break; |
136 | case Coda::CodaEvent::ProcessExitedEvent: |
137 | handleAppExited(event); |
138 | break; |
139 | default: |
140 | reportMessage(tr("CODA unhandled event: Type: %1 Message: %2").arg(event.type()).arg(event.toString())); |
141 | break; |
142 | } |
143 | } |
144 | |
145 | void CodaSignalHandler::terminate() |
146 | { |
147 | if (d->codaDevice) { |
148 | disconnect(d->codaDevice.data(), 0, this, 0); |
149 | SymbianUtils::SymbianDeviceManager::instance()->releaseCodaDevice(d->codaDevice); |
150 | } |
151 | } |
152 | |
153 | void CodaSignalHandler::finished() |
154 | { |
155 | if (d->eventLoop) |
156 | d->eventLoop->exit(); |
157 | } |
158 | |
159 | void CodaSignalHandler::timeout() |
160 | { |
161 | reportError(tr("Unable to connect to CODA device. Operation timed out.")); |
162 | } |
163 | |
164 | int CodaSignalHandler::run() |
165 | { |
166 | d->codaDevice = SymbianUtils::SymbianDeviceManager::instance()->getCodaDevice(d->serialPortName); |
167 | bool ok = d->codaDevice && d->codaDevice->device()->isOpen(); |
168 | if (!ok) { |
169 | QString deviceError = "No such port"; |
170 | if (d->codaDevice) |
171 | deviceError = d->codaDevice->device()->errorString(); |
172 | reportError(tr("Could not open serial device: %1").arg(deviceError)); |
173 | SymbianUtils::SymbianDeviceManager::instance()->releaseCodaDevice(d->codaDevice); |
174 | return 1; |
175 | } |
176 | |
177 | TextTraceHandler *traceHandler = new TextTraceHandler( |
178 | SymbianUtils::SymbianDeviceManager::instance()->getOstChannel(d->serialPortName, 2), this); |
179 | |
180 | if (d->loglevel > 1) { |
181 | d->codaDevice->setVerbose(1); |
182 | } |
183 | |
184 | connect(d->codaDevice.data(), SIGNAL(error(const QString &)), this, SLOT(error(const QString &))); |
185 | connect(d->codaDevice.data(), SIGNAL(logMessage(const QString &)), this, SLOT(logMessage(const QString &))); |
186 | connect(d->codaDevice.data(), SIGNAL(serialPong(const QString &)), this, SLOT(serialPong(const QString &))); |
187 | connect(d->codaDevice.data(), SIGNAL(tcfEvent(const Coda::CodaEvent &)), this, SLOT(tcfEvent(const Coda::CodaEvent &))); |
188 | connect(this, SIGNAL(done()), this, SLOT(finished())); |
189 | |
190 | d->codaDevice->sendSerialPing(false); |
191 | QTimer timer; |
192 | if (d->timeout > 0) { |
193 | connect(&timer, SIGNAL(timeout()), this, SLOT(timeout())); |
194 | timer.setSingleShot(true); |
195 | timer.start(d->timeout); |
196 | } |
197 | d->eventLoop = new QEventLoop(); |
198 | d->eventLoop->exec(); |
199 | timer.stop(); |
200 | int result = d->result; |
201 | reportMessage(tr("Done.")); |
202 | |
203 | delete traceHandler; |
204 | disconnect(d->codaDevice.data(), 0, this, 0); |
205 | SymbianUtils::SymbianDeviceManager::instance()->releaseCodaDevice(d->codaDevice); |
206 | |
207 | return result; |
208 | } |
209 | |
210 | void CodaSignalHandler::setActionType(CodaAction action) |
211 | { |
212 | d->action = CodaAction(d->action | action); |
213 | } |
214 | |
215 | void CodaSignalHandler::setAppFileName(const QString &fileName) |
216 | { |
217 | d->appFileName = fileName; |
218 | } |
219 | |
220 | void CodaSignalHandler::setCodaDevice(SymbianUtils::CodaDevicePtr &codaDevice) |
221 | { |
222 | d->codaDevice = codaDevice; |
223 | } |
224 | |
225 | void CodaSignalHandler::setCommandLineArgs(const QString &args) |
226 | { |
227 | d->commandLineArgs = args; |
228 | } |
229 | |
230 | void CodaSignalHandler::setCopyFileName(const QString &srcName, const QString &dstName) |
231 | { |
232 | d->copySrcFileName = srcName; |
233 | d->copyDstFileName = dstName; |
234 | } |
235 | |
236 | void CodaSignalHandler::setDownloadFileName(const QString &srcName, const QString &dstName) |
237 | { |
238 | d->dlSrcFileName = srcName; |
239 | d->dlDstFileName = dstName; |
240 | } |
241 | |
242 | void CodaSignalHandler::setLogLevel(int level) |
243 | { |
244 | d->loglevel = level; |
245 | } |
246 | |
247 | void CodaSignalHandler::setSerialPortName(const QString &serialPortName) |
248 | { |
249 | d->serialPortName = serialPortName; |
250 | } |
251 | |
252 | void CodaSignalHandler::setTimeout(const int msec) |
253 | { |
254 | d->timeout = msec; |
255 | } |
256 | |
257 | void CodaSignalHandler::closeFile() |
258 | { |
259 | d->remoteFile.reset(); |
260 | if (!d->codaDevice) { |
261 | emit done(); |
262 | return; |
263 | } |
264 | |
265 | d->codaDevice->sendFileSystemCloseCommand(Coda::CodaCallback(this, &CodaSignalHandler::handleFileSystemClose), |
266 | d->remoteFileHandle); |
267 | } |
268 | |
269 | void CodaSignalHandler::handleConnected(const Coda::CodaEvent &event) |
270 | { |
271 | if (d->connected) |
272 | return; |
273 | |
274 | const Coda::CodaLocatorHelloEvent &hEvent = static_cast<const Coda::CodaLocatorHelloEvent &>(event); |
275 | QStringList services = hEvent.services(); |
276 | if (services.contains("DebugSessionControl")) { |
277 | d->debugSessionControl = true; |
278 | } |
279 | d->connected = true; |
280 | handleActions(); |
281 | } |
282 | |
283 | void CodaSignalHandler::handleActions() |
284 | { |
285 | if (d->action & ActionCopy) { |
286 | initFileSending(); |
287 | } else if (d->action & ActionInstall) { |
288 | initFileInstallation(); |
289 | } else if (d->action & ActionRun) { |
290 | initAppRunning(); |
291 | } else if (d->action & ActionDownload) { |
292 | initFileDownloading(); |
293 | } else { |
294 | emit done(); |
295 | } |
296 | } |
297 | |
298 | void CodaSignalHandler::handleAppExited(const Coda::CodaEvent &event) |
299 | { |
300 | const Coda::CodaProcessExitedEvent &pEvent = static_cast<const Coda::CodaProcessExitedEvent &>(event); |
301 | QString id = pEvent.idString(); |
302 | if (!id.compare(d->appID, Qt::CaseInsensitive)) { |
303 | d->codaDevice->sendDebugSessionControlSessionEndCommand(Coda::CodaCallback(this, &CodaSignalHandler::handleDebugSessionControlEnd)); |
304 | d->action = static_cast<CodaAction>(d->action & ~ActionRun); |
305 | handleActions(); |
306 | } |
307 | } |
308 | |
309 | void CodaSignalHandler::handleAppRunning(const Coda::CodaCommandResult &result) |
310 | { |
311 | if (result.type == Coda::CodaCommandResult::SuccessReply) { |
312 | reportMessage(tr("Running...")); |
313 | |
314 | Coda::JsonValue value = result.values.at(0); |
315 | readAppId(value); |
316 | } else { |
317 | reportError(tr("Launch failed: %1").arg(result.toString())); |
318 | } |
319 | } |
320 | |
321 | void CodaSignalHandler::handleDebugSessionControlEnd(const Coda::CodaCommandResult &result) |
322 | { |
323 | if (result.type == Coda::CodaCommandResult::SuccessReply) { |
324 | // nothing to do |
325 | } |
326 | } |
327 | |
328 | void CodaSignalHandler::handleDebugSessionControlStart(const Coda::CodaCommandResult &result) |
329 | { |
330 | if (result.type == Coda::CodaCommandResult::SuccessReply) { |
331 | d->codaDevice->sendRunProcessCommand(Coda::CodaCallback(this, &CodaSignalHandler::handleAppRunning), |
332 | d->appFileName.toAscii(), |
333 | d->commandLineArgs.split( |
334 | reportMessage(tr("Launching %1...").arg(QFileInfo(d->appFileName).fileName())); |
335 | } else { |
336 | reportError(tr("Failed to start CODA debug session control.")); |
337 | } |
338 | } |
339 | |
340 | void CodaSignalHandler::handleFileSystemClose(const Coda::CodaCommandResult &result) |
341 | { |
342 | if (result.type == Coda::CodaCommandResult::SuccessReply) { |
343 | reportMessage(tr("File closed.")); |
344 | if (d->action & ActionCopy) { |
345 | d->action = static_cast<CodaAction>(d->action & ~ActionCopy); |
346 | } else if (d->action & ActionDownload) { |
347 | d->action = static_cast<CodaAction>(d->action & ~ActionDownload); |
348 | } |
349 | handleActions(); |
350 | } else { |
351 | reportError(tr("Failed to close the remote file: %1").arg(result.toString())); |
352 | } |
353 | } |
354 | |
355 | void CodaSignalHandler::handleFileSystemOpen(const Coda::CodaCommandResult &result) |
356 | { |
357 | if (result.type != Coda::CodaCommandResult::SuccessReply) { |
358 | reportError(tr("Could not open remote file: %1").arg(result.errorString())); |
359 | return; |
360 | } |
361 | |
362 | if (result.values.size() < 1 || result.values.at(0).data().isEmpty()) { |
363 | reportError(tr("Internal error: No filehandle obtained")); |
364 | return; |
365 | } |
366 | |
367 | if (d->action & ActionCopy) { |
368 | d->remoteFileHandle = result.values.at(0).data(); |
369 | d->remoteFile.reset(new QFile(d->copySrcFileName)); |
370 | if (!d->remoteFile->open(QIODevice::ReadOnly)) { // Should not fail, was checked before |
371 | reportError(tr("Could not open local file %1").arg(d->copySrcFileName)); |
372 | return; |
373 | } |
374 | putSendNextChunk(); |
375 | } else if (d->action & ActionDownload) { |
376 | d->remoteFileHandle = result.values.at(0).data(); |
377 | d->localFile.reset(new QFile(d->dlDstFileName)); |
378 | // remove any existing file with the same name |
379 | if (d->localFile->exists() && !d->localFile->remove()) { |
380 | reportError(tr("Could not create host file: %1 due to error: %2").arg(d->localFile->fileName(), d->localFile->errorString())); |
381 | return; |
382 | } |
383 | // open local file in write-only mode |
384 | if (!d->localFile->open(QFile::WriteOnly)) { |
385 | reportError(tr("Could not open host file for writing: %1 due to error: %2").arg(d->localFile->fileName(), d->localFile->errorString())); |
386 | return; |
387 | } |
388 | d->codaDevice->sendFileSystemFstatCommand(Coda::CodaCallback(this, &CodaSignalHandler::handleFileSystemStart), |
389 | d->remoteFileHandle); |
390 | } |
391 | } |
392 | |
393 | void CodaSignalHandler::handleFileSystemRead(const Coda::CodaCommandResult &result) |
394 | { |
395 | if (result.type != Coda::CodaCommandResult::SuccessReply || result.values.size() != 2) { |
396 | reportError(tr("Could not read remote file: %1").arg(result.errorString())); |
397 | return; |
398 | } |
399 | |
400 | QByteArray data = QByteArray::fromBase64(result.values.at(0).data()); |
401 | bool eof = result.values.at(1).toVariant().toBool(); |
402 | qint64 written = d->localFile->write(data); |
403 | if (written < 0) { |
404 | reportError(tr("Could not write data to host file: %1 due to error: %2").arg(d->localFile->fileName(), d->localFile->errorString())); |
405 | return; |
406 | } |
407 | |
408 | d->remoteBytesLeft -= written; |
409 | if (!eof && d->remoteBytesLeft > 0) { |
410 | readNextChunk(); |
411 | } |
412 | else { |
413 | d->localFile->flush(); |
414 | d->localFile->close(); |
415 | closeFile(); |
416 | } |
417 | } |
418 | |
419 | void CodaSignalHandler::handleFileSystemStart(const Coda::CodaCommandResult &result) |
420 | { |
421 | if (result.type != Coda::CodaCommandResult::SuccessReply) { |
422 | reportError(tr("Could not open remote file: %1").arg(result.errorString())); |
423 | return; |
424 | } |
425 | |
426 | if (result.values.size() < 1 || result.values.at(0).children().isEmpty()) { |
427 | reportError(tr("Could not get file attributes")); |
428 | return; |
429 | } |
430 | |
431 | Coda::JsonValue val = result.values.at(0).findChild("Size"); |
432 | d->remoteFileSize = val.isValid() ? val.data().toLong() : -1L; |
433 | if (d->remoteFileSize < 0) { |
434 | reportError(tr("Could not get file size")); |
435 | return; |
436 | } |
437 | |
438 | d->remoteBytesLeft = d->remoteFileSize; |
439 | readNextChunk(); |
440 | } |
441 | |
442 | void CodaSignalHandler::handleFileSystemWrite(const Coda::CodaCommandResult &result) |
443 | { |
444 | // Close remote file even if copy fails |
445 | d->putWriteOk = result; |
446 | if (!d->putWriteOk) { |
447 | QString fileName = QFileInfo(d->copyDstFileName).fileName(); |
448 | reportError(tr("Could not write to file %1 on device: %2").arg(fileName).arg(result.errorString())); |
449 | } |
450 | |
451 | if (!d->putWriteOk || d->putLastChunkSize < d->putChunkSize) { |
452 | closeFile(); |
453 | } else { |
454 | putSendNextChunk(); |
455 | } |
456 | } |
457 | |
458 | void CodaSignalHandler::handleSymbianInstall(const Coda::CodaCommandResult &result) |
459 | { |
460 | if (result.type == Coda::CodaCommandResult::SuccessReply) { |
461 | reportMessage(tr("Installation has finished.")); |
462 | d->action = static_cast<CodaAction>(d->action & ~ActionInstall); |
463 | handleActions(); |
464 | } else { |
465 | reportError(tr("Installation failed: %1").arg(result.errorString())); |
466 | } |
467 | } |
468 | |
469 | void CodaSignalHandler::initAppRunning() |
470 | { |
471 | if (!d->codaDevice || d->appFileName.isEmpty()) { |
472 | emit done(); |
473 | return; |
474 | } |
475 | |
476 | if (!d->debugSessionControl) { |
477 | reportError(tr("CODA DebugSessionControl service not found, please update to CODA v4.0.23 or later.")); |
478 | } |
479 | |
480 | d->codaDevice->sendDebugSessionControlSessionStartCommand(Coda::CodaCallback(this, &CodaSignalHandler::handleDebugSessionControlStart)); |
481 | } |
482 | |
483 | void CodaSignalHandler::initFileDownloading() |
484 | { |
485 | if (!d->codaDevice || d->dlSrcFileName.isEmpty()) { |
486 | emit done(); |
487 | return; |
488 | } |
489 | |
490 | d->codaDevice->sendFileSystemOpenCommand(Coda::CodaCallback(this, &CodaSignalHandler::handleFileSystemOpen), |
491 | d->dlSrcFileName.toAscii(), Coda::CodaDevice::FileSystem_TCF_O_READ); |
492 | reportMessage(tr("Downloading %1...").arg(QFileInfo(d->dlSrcFileName).fileName())); |
493 | } |
494 | |
495 | void CodaSignalHandler::initFileInstallation() |
496 | { |
497 | if (!d->codaDevice || d->copyDstFileName.isEmpty()) { |
498 | emit done(); |
499 | return; |
500 | } |
501 | |
502 | QString installationDrive = "C"; |
503 | d->codaDevice->sendSymbianInstallSilentInstallCommand(Coda::CodaCallback(this, &CodaSignalHandler::handleSymbianInstall), |
504 | d->copyDstFileName.toAscii(), |
505 | installationDrive.toAscii()); |
506 | reportMessage(tr("Installing package \"%1\" on drive %2...").arg(QFileInfo(d->copyDstFileName).fileName(), installationDrive)); |
507 | } |
508 | |
509 | void CodaSignalHandler::initFileSending() |
510 | { |
511 | if (!d->codaDevice || d->copySrcFileName.isEmpty()) { |
512 | emit done(); |
513 | return; |
514 | } |
515 | |
516 | const unsigned flags = |
517 | Coda::CodaDevice::FileSystem_TCF_O_WRITE |
518 | |Coda::CodaDevice::FileSystem_TCF_O_CREAT |
519 | |Coda::CodaDevice::FileSystem_TCF_O_TRUNC; |
520 | d->putWriteOk = false; |
521 | d->codaDevice->sendFileSystemOpenCommand(Coda::CodaCallback(this, &CodaSignalHandler::handleFileSystemOpen), |
522 | d->copyDstFileName.toAscii(), flags); |
523 | reportMessage(tr("Copying %1...").arg(QFileInfo(d->copyDstFileName).fileName())); |
524 | } |
525 | |
526 | void CodaSignalHandler::putSendNextChunk() |
527 | { |
528 | if (!d->codaDevice || !d->remoteFile) { |
529 | emit done(); |
530 | return; |
531 | } |
532 | |
533 | // Read and send off next chunk |
534 | const quint64 pos = d->remoteFile->pos(); |
535 | const QByteArray data = d->remoteFile->read(d->putChunkSize); |
536 | if (data.isEmpty()) { |
537 | d->putWriteOk = true; |
538 | closeFile(); |
539 | } else { |
540 | d->putLastChunkSize = data.size(); |
541 | d->codaDevice->sendFileSystemWriteCommand(Coda::CodaCallback(this, &CodaSignalHandler::handleFileSystemWrite), |
542 | d->remoteFileHandle, data, unsigned(pos)); |
543 | } |
544 | } |
545 | |
546 | void CodaSignalHandler::readNextChunk() |
547 | { |
548 | const quint64 pos = d->remoteFileSize - d->remoteBytesLeft; |
549 | const quint64 size = qMin(d->remoteBytesLeft, DEFAULT_CHUNK_SIZE); |
550 | d->codaDevice->sendFileSystemReadCommand(Coda::CodaCallback(this, &CodaSignalHandler::handleFileSystemRead), |
551 | d->remoteFileHandle, pos, size); |
552 | } |
553 | |
554 | void CodaSignalHandler::readAppId(Coda::JsonValue value) |
555 | { |
556 | if (value.isObject()) { |
557 | Coda::JsonValue idValue = value.findChild("ID"); |
558 | if (idValue.isString()) { |
559 | d->appID = idValue.data(); |
560 | return; |
561 | } |
562 | } |
563 | |
564 | reportError(tr("Could not get process ID of %1.").arg(QFileInfo(d->appFileName).fileName())); |
565 | } |
566 | |
567 | void CodaSignalHandler::reportError(const QString &message) |
568 | { |
569 | d->err << message << endl; |
570 | d->result = 1; |
571 | emit done(); |
572 | } |
573 | |
574 | void CodaSignalHandler::reportMessage(const QString &message) |
575 | { |
576 | if (d->loglevel > 0) |
577 | d->out << message << endl; |
578 | } |
579 | |
580 | CodaSignalHandler::CodaSignalHandler() |
581 | : d(new CodaSignalHandlerPrivate()) |
582 | { |
583 | } |
584 | |
585 | CodaSignalHandler::~CodaSignalHandler() |
586 | { |
587 | delete d; |
588 | } |
589 | |
590 |
Warning: That file was not part of the compilation database. It may have many parsing errors.