You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
kscope/src/frontend.cpp

366 lines
9.5 KiB

/***************************************************************************
*
* Copyright (C) 2005 Elad Lahav (elad_lahav@users.sourceforge.net)
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
***************************************************************************/
#include <tqfileinfo.h>
#include <tqdir.h>
#include <tdelocale.h>
#include "frontend.h"
/**
* Class constructor.
* @param nRecordSize The number of fields in each record
* @param bAutoDelete (Optional) true to delete the object when the process
* terminates, false (default) otherwise
*/
Frontend::Frontend(uint nRecordSize, bool bAutoDelete) : TDEProcess(),
m_nRecords(0),
m_pHeadToken(NULL),
m_pTailToken(NULL),
m_pCurToken(NULL),
m_bAutoDelete(bAutoDelete),
m_bInToken(false),
m_nRecordSize(nRecordSize)
{
// Parse data on the standard output
connect(this, TQ_SIGNAL(receivedStdout(TDEProcess*, char*, int)), this,
TQ_SLOT(slotReadStdout(TDEProcess*, char*, int)));
// Parse data on the standard error
connect(this, TQ_SIGNAL(receivedStderr(TDEProcess*, char*, int)), this,
TQ_SLOT(slotReadStderr(TDEProcess*, char*, int)));
// Delete the process object when the process exits
connect(this, TQ_SIGNAL(processExited(TDEProcess*)), this,
TQ_SLOT(slotProcessExit(TDEProcess*)));
}
/**
* Class destructor.
*/
Frontend::~Frontend()
{
// Delete all pending tokens
while (m_pHeadToken)
removeToken();
}
/**
* Executes the back-end process.
* @param sName The name of the process (for error messages)
* @param slArgs A list containing the command-line arguments
* @param sWorkDir (Optional) working directory
* @param bBlock (Optional) true to block, false otherwise
* @return true if the process was executed successfully, false otherwise
*/
bool Frontend::run(const TQString& sName, const TQStringList& slArgs,
const TQString& sWorkDir, bool bBlock)
{
// Cannot start if another controlled process is currently running
if (isRunning()) {
m_sError = i18n("Cannot restart while another process is still "
"running");
return false;
}
// Reset variables
m_nRecords = 0;
m_bKilled = false;
// Setup the command-line arguments
clearArguments();
*this << slArgs;
// Set the working directory, if requested
if (!sWorkDir.isEmpty())
setWorkingDirectory(sWorkDir);
// Execute the child process
if (!start(bBlock ? TDEProcess::Block : TDEProcess::NotifyOnExit,
TDEProcess::All)) {
m_sError = sName + i18n(": Failed to start process");
return false;
}
m_sError = i18n("No error");
return true;
}
/**
* Kills the process, and emits the aborted() signal.
* This function should not be called unless the process needs to be
* interrupted.
*/
void Frontend::kill()
{
m_bKilled = true;
TDEProcess::kill();
emit aborted();
}
/**
* Appends a token to the end of the token list.
* @param pToken The token to add
*/
void Frontend::addToken(FrontendToken* pToken)
{
// Check if this is the firt token
if (m_pHeadToken == NULL) {
m_pHeadToken = pToken;
m_pTailToken = pToken;
}
else {
// Not the first token, append and reset the tail token
m_pTailToken->m_pNext = pToken;
m_pTailToken = pToken;
}
}
/**
* Removes and deletes the token at the head of the token list.
*/
void Frontend::removeToken()
{
FrontendToken* pToken;
if (m_pHeadToken == NULL)
return;
pToken = m_pHeadToken;
m_pHeadToken = m_pHeadToken->m_pNext;
delete pToken;
if (m_pHeadToken == NULL)
m_pTailToken = NULL;
}
/**
* Removes tokens from the head of the list, according to the size of a
* record.
*/
void Frontend::removeRecord()
{
uint i;
for (i = 0; (i < m_nRecordSize) && (m_pHeadToken != NULL); i++)
removeToken();
}
/**
* Extracts tokens of text out of a given buffer.
* @param ppBuf Points to the buffer to parse, and is set to the
* beginning of the next token, upon return
* @param pBufSize Points to the size of the buffer, and is set to the
* remaining size, upon return
* @param sResult Holds the token's text, upon successful return
* @param delim Holds the delimiter by which the token's end was
* determined
* @return true if a token was extracted up to the given delimter(s), false
* if the buffer ended before a delimiter could be identified
*/
bool Frontend::tokenize(char** ppBuf, int* pBufSize, TQString& sResult,
ParserDelim& delim)
{
int nSize;
char* pBuf;
bool bDelim, bWhiteSpace, bFoundToken = false;
// Iterate buffer
for (nSize = *pBufSize, pBuf = *ppBuf; (nSize > 0) && !bFoundToken;
nSize--, pBuf++) {
// Test if this is a delimiter character
switch (*pBuf) {
case '\n':
bDelim = ((m_delim & Newline) != 0);
bWhiteSpace = true;
delim = Newline;
break;
case ' ':
bDelim = ((m_delim & Space) != 0);
bWhiteSpace = true;
delim = Space;
break;
case '\t':
bDelim = ((m_delim & Tab) != 0);
bWhiteSpace = true;
delim = Tab;
break;
default:
bDelim = false;
bWhiteSpace = false;
}
if (m_bInToken && bDelim) {
m_bInToken = false;
*pBuf = 0;
bFoundToken = true;
}
else if (!m_bInToken && !bWhiteSpace) {
m_bInToken = true;
*ppBuf = pBuf;
}
}
// Either a token was found, or the search through the buffer was
// finished without a delimiter character
if (bFoundToken) {
sResult = TQString::fromLocal8Bit(*ppBuf);
*ppBuf = pBuf;
*pBufSize = nSize;
}
else if (m_bInToken) {
sResult = TQString::fromLocal8Bit(*ppBuf, *pBufSize);
}
else {
sResult = TQString::null;
}
return bFoundToken;
}
/**
* Handles text sent by the back-end process to the standard error stream.
* By default, this method emits the error() signal with the given text.
* @param sText The text sent to the standard error stream
*/
void Frontend::parseStderr(const TQString& sText)
{
emit error(sText);
}
/**
* Deletes the process object upon the process' exit.
*/
void Frontend::slotProcessExit(TDEProcess*)
{
// Allow specialised clean-up by inheriting classes
finalize();
// Signal the process has terminated
emit finished(m_nRecords);
// Delete the object, if required
if (m_bAutoDelete)
delete this;
}
/**
* Reads data written on the standard output by the controlled process.
* This is a private slot called attached to the readyReadStdout() signal of
* the controlled process, which means that it is called whenever data is
* ready to be read from the process' stream.
* The method reads whatever data is queued, and sends it to be interpreted
* by parseStdout().
*/
void Frontend::slotReadStdout(TDEProcess*, char* pBuffer, int nSize)
{
char* pLocalBuf;
TQString sToken;
bool bTokenEnded;
ParserDelim delim;
// Do nothing if waiting for process to die
if (m_bKilled)
return;
pLocalBuf = pBuffer;
// Iterate over the given buffer
while (nSize > 0) {
// Create a new token, if the last iteration has completed one
if (m_pCurToken == NULL)
m_pCurToken = new FrontendToken();
// Extract text until the requested delimiter
bTokenEnded = tokenize(&pLocalBuf, &nSize, sToken, delim);
// Add the extracted text to the current token
m_pCurToken->m_sData += sToken;
// If the buffer has ended before the requested delimiter, we need
// to wait for more output from the process
if (!bTokenEnded)
return;
// Call the process-specific parser function
switch (parseStdout(m_pCurToken->m_sData, delim)) {
case DiscardToken:
// Token should not be saved
delete m_pCurToken;
break;
case AcceptToken:
// Store token in linked list
addToken(m_pCurToken);
break;
case RecordReady:
// Store token, and notify the target object that an entry can
// be read
m_nRecords++;
addToken(m_pCurToken);
emit dataReady(m_pHeadToken);
// Delete all tokens in the entry
removeRecord();
break;
case Abort:
kill();
nSize = 0;
break;
}
m_pCurToken = NULL;
}
}
/**
* Reads data written on the standard error by the controlled process.
* This is a private slot called attached to the readyReadStderr() signal of
* the controlled process, which means that it is called whenever data is
* ready to be read from the process' stream.
* The method reads whatever data is queued, and sends it to be interpreted
* by parseStderr().
*/
void Frontend::slotReadStderr(TDEProcess*, char* pBuffer, int nSize)
{
TQString sBuf;
// Do nothing if waiting for process to die
if (m_bKilled)
return;
sBuf = TQString::fromLocal8Bit(pBuffer, nSize);
parseStderr(sBuf);
}
#include "frontend.moc"