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/cscopefrontend.cpp

525 lines
13 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 <tqtimer.h>
#include <tdeconfig.h>
#include <tdemessagebox.h>
#include <tdelocale.h>
#include <tdeglobalsettings.h>
#include "cscopefrontend.h"
#include "kscopeconfig.h"
#include "configfrontend.h"
#define BUILD_STR "Building symbol database %d of %d"
#define SEARCH_STR "Search %d of %d"
#define INV_STR "Possible references retrieved %d of %d"
#define REGEXP_STR "Symbols matched %d of %d"
#define SEARCHEND_STR "%d lines"
TQString CscopeFrontend::s_sProjPath;
uint CscopeFrontend::s_nProjArgs;
uint CscopeFrontend::s_nSupArgs;
/**
* Class constructor.
* @param bAutoDelete true to delete the object once the process has
* terminated, false otherwise
*/
CscopeFrontend::CscopeFrontend(bool bAutoDelete) :
Frontend(CSCOPE_RECORD_SIZE, bAutoDelete),
m_state(Unknown),
m_sErrMsg(""),
m_bRebuildOnExit(false)
{
}
/**
* Class destructor.
*/
CscopeFrontend::~CscopeFrontend()
{
}
/**
* Executes a Cscope process using the given command line arguments.
* The full path to the Cscope executable should be set in the "Path" key
* under the "Cscope" group.
* @param slArgs Command line arguments for Cscope
* @return true if successful, false otherwise
*/
bool CscopeFrontend::run(const TQStringList& slArgs)
{
TQStringList slCmdLine;
// Set the command line arguments
slCmdLine.append(Config().getCscopePath());
slCmdLine += slArgs;
// Use verbose mode, if supported
if (s_nSupArgs & VerboseOut)
slCmdLine << "-v";
// Project-specific options
if (s_nProjArgs & Kernel)
slCmdLine << "-k";
if (s_nProjArgs & InvIndex)
slCmdLine << "-q";
if (s_nProjArgs & NoCompression)
slCmdLine << "-c";
if (s_nProjArgs & s_nSupArgs & SlowPathDef)
slCmdLine << "-D";
// Run a new process
if (!Frontend::run("cscope", slCmdLine, s_sProjPath)) {
emit aborted();
return false;
}
return true;
}
/**
* Executes a Cscope query.
* A query is composed of a numeric type and a query text, which are written
* to the stndard input of the currently running Cscope process.
* @param nType The type of query to run
* @param sText The query's text
* @param bCase true for case-sensitive queries, false otherwise
* @param nMaxRecords The maximal number of records to return (abort if this
* number is exceeded)
*/
void CscopeFrontend::query(uint nType, const TQString& sText, bool bCase,
uint nMaxRecords)
{
TQString sQuery;
TQStringList slArgs;
m_nMaxRecords = nMaxRecords;
// Create the Cscope command line
slArgs.append(TQString("-L") + TQString::number(nType));
slArgs.append(sText);
slArgs.append("-d");
if (!bCase)
slArgs.append("-C");
run(slArgs);
// Initialise stdout parsing
m_state = SearchSymbol;
m_delim = WSpace;
emit progress(0, 1);
}
/**
* Rebuilds the symbol database of the current project.
*/
void CscopeFrontend::rebuild()
{
TQStringList slArgs;
// If a process is already running, kill it start a new one
if (isRunning()) {
m_bRebuildOnExit = true;
kill();
return;
}
// Run the database building process
slArgs.append("-b");
run(slArgs);
// Initialise output parsing
m_state = BuildStart;
m_delim = Newline;
emit progress(0, 1);
}
/**
* Sets default parameters for all CscopeFrontend projects based on the
* current project.
* @param sProjPath The full path of the project's directory
* @param nArgs Project-specific command-line arguments
*/
void CscopeFrontend::init(const TQString& sProjPath, uint nArgs)
{
s_sProjPath = sProjPath;
s_nProjArgs = nArgs;
}
/**
* Stops a Cscope action.
*/
void CscopeFrontend::slotCancel()
{
kill();
}
/**
* Parses the output of a Cscope process.
* Implements a state machine, where states correspond to the output of the
* controlled Cscope process.
* @param sToken The current token read (the token delimiter is determined
* by the current state)
* @return A value indicating the way this token should be treated: dropped,
* added to the token queue, or finishes a new record
*/
Frontend::ParseResult CscopeFrontend::parseStdout(TQString& sToken,
ParserDelim /* ignored */)
{
int nFiles, nTotal, nRecords;
ParseResult result = DiscardToken;
ParserState stPrev;
// Remember previous state
stPrev = m_state;
// Handle the token according to the current state
switch (m_state) {
case BuildStart:
if (sToken == "Building cross-reference...") {
m_state = BuildSymbol;
m_delim = WSpace;
}
else if (sToken == "Building inverted index...") {
emit buildInvIndex();
}
result = DiscardToken;
break;
case BuildSymbol:
// A single angle bracket is the prefix of a progress indication,
// while double brackets is Cscope's prompt for a new query
if (sToken == ">") {
m_state = Building;
m_delim = Newline;
}
result = DiscardToken;
break;
case Building:
// Try to get building progress
if (sscanf(sToken.latin1(), BUILD_STR, &nFiles, &nTotal) == 2) {
emit progress(nFiles, nTotal);
// Check for last progress message
if (nFiles == nTotal) {
m_state = BuildStart;
m_delim = Newline;
result = DiscardToken;
break;
}
}
// Wait for another progress line or the "ready" symbol
m_state = BuildSymbol;
m_delim = WSpace;
result = DiscardToken;
break;
case SearchSymbol:
// Check for more search progress, or the end of the search,
// designated by a line in the format of "cscope: X lines"
if (sToken == ">") {
m_state = Searching;
m_delim = Newline;
result = DiscardToken;
break;
}
else if (sToken == "cscope:") {
m_state = SearchEnd;
m_delim = Newline;
result = DiscardToken;
break;
}
case File:
// Is this the first entry? If so, signal that the query is complete
if (stPrev != LineText)
emit progress(1, 1);
// Treat the token as the name of the file in this record
m_state = Func;
result = AcceptToken;
break;
case Searching:
// Try to get the search progress value (ignore other messages)
if ((sscanf(sToken.latin1(), SEARCH_STR, &nFiles, &nTotal) == 2) ||
(sscanf(sToken.latin1(), INV_STR, &nFiles, &nTotal) == 2) ||
(sscanf(sToken.latin1(), REGEXP_STR, &nFiles, &nTotal) == 2)) {
emit progress(nFiles, nTotal);
}
m_state = SearchSymbol;
m_delim = WSpace;
result = DiscardToken;
break;
case SearchEnd:
// Get the number of results found in this search
if ((sscanf(sToken.latin1(), SEARCHEND_STR, &nRecords) == 1) &&
(m_nMaxRecords > 0) &&
(nRecords > m_nMaxRecords)) {
result = Abort;
}
else {
m_state = File;
m_delim = WSpace;
result = DiscardToken;
}
break;
case Func:
// Treat the token as the name of the function in this record
if (sToken.toInt()) {
// In case of a global definition, there is no function name, and
// instead the line number is given immediately
m_state = LineText;
m_delim = Newline;
}
else {
// Not a number, it is the name of the function
m_state = Line;
}
result = AcceptToken;
break;
case Line:
// Treat the token as the line number in this record
m_state = LineText;
m_delim = Newline;
result = AcceptToken;
break;
case LineText:
// Treat the token as the text of this record, and report a new
// record
m_state = File;
m_delim = WSpace;
result = RecordReady;
break;
default:
// Do nothing (prevents a compilation warning for unused enum values)
break;
}
return result;
}
/**
* Handles Cscope messages sent to the standard error stream.
* @param sText The error message text
*/
void CscopeFrontend::parseStderr(const TQString& sText)
{
// Wait for a complete line to arrive
m_sErrMsg += sText;
if (!sText.endsWith("\n"))
return;
// Display the error message
emit error(m_sErrMsg);
// Line displayed, reset the text accumulator
m_sErrMsg = "";
}
/**
* Called when the underlying process exits.
* Checks if the rebuild flag was raised, and if so restarts the building
* process.
*/
void CscopeFrontend::finalize()
{
// Reset the parser state machine
m_state = Unknown;
// Restart the building process, if required
if (m_bRebuildOnExit) {
m_bRebuildOnExit = false;
rebuild();
}
}
/**
* Class constructor.
* @param pMainWidget The parent widget to use for the progress bar and
* label
*/
CscopeProgress::CscopeProgress(TQWidget* pMainWidget) : TQObject(),
m_pMainWidget(pMainWidget),
m_pProgressBar(NULL),
m_pLabel(NULL)
{
}
/**
* Class destructor.
*/
CscopeProgress::~CscopeProgress()
{
}
/**
* Displays query progress information.
* If the progress value is below the expected final value, a progress bar is
* used to show the advance of the query process. Otherwise, a label is
* displayed asking the user to wait ahile the query output is processed.
* @param nProgress The current progress value
* @param nTotal The expected final value
*/
void CscopeProgress::setProgress(int nProgress, int nTotal)
{
// Was the final value is reached?
if (nProgress == nTotal) {
// Destroy the progress bar
if (m_pProgressBar != NULL) {
delete m_pProgressBar;
m_pProgressBar = NULL;
}
// Show the "Please wait..." label
if (m_pLabel == NULL) {
m_pLabel = new TQLabel(i18n("Processing query results, "
"please wait..."), m_pMainWidget);
m_pLabel->setFrameStyle(TQFrame::Box | TQFrame::Plain);
m_pLabel->setLineWidth(1);
m_pLabel->adjustSize();
m_pLabel->setPaletteBackgroundColor(
TDEGlobalSettings::highlightColor());
m_pLabel->setPaletteForegroundColor(
TDEGlobalSettings::highlightedTextColor());
TQTimer::singleShot(1000, this, TQ_SLOT(slotShowLabel()));
}
return;
}
// Create the progress bar, if it does not exist.
// Note that the progress bar will only be displayed one second after the
// first progress signal is received. Thus the bar will not be displayed
// on very short queries.
if (m_pProgressBar == NULL) {
m_pProgressBar = new TQProgressBar(m_pMainWidget);
TQTimer::singleShot(1000, this, TQ_SLOT(slotShowProgressBar()));
}
// Set the current progress value
m_pProgressBar->setProgress(nProgress, nTotal);
}
/**
* detsroys any progress widgets when the process is terminated.
*/
void CscopeProgress::finished()
{
// Destroy the progress bar
if (m_pProgressBar != NULL) {
delete m_pProgressBar;
m_pProgressBar = NULL;
}
// Destroy the label
if (m_pLabel != NULL) {
delete m_pLabel;
m_pLabel = NULL;
}
}
/**
* Shows the progress bar.
* This slot is connected to a timer activated when the first progress signal
* is received.
*/
void CscopeProgress::slotShowProgressBar()
{
if (m_pProgressBar != NULL)
m_pProgressBar->show();
}
/**
* Shows the "Please wait...".
* This slot is connected to a timer activated when the progress bar
* reaches its final value.
*/
void CscopeProgress::slotShowLabel()
{
if (m_pLabel != NULL)
m_pLabel->show();
}
void CscopeVerifier::verify()
{
ConfigFrontend* pConf;
pConf = new ConfigFrontend(true);
connect(pConf, TQ_SIGNAL(result(uint, const TQString&)), this,
TQ_SLOT(slotConfigResult(uint, const TQString&)));
connect(pConf, TQ_SIGNAL(finished(uint)), this, TQ_SLOT(slotFinished()));
pConf->run(Config().getCscopePath(), "", "", true);
}
void CscopeVerifier::slotConfigResult(uint nType, const TQString& sResult)
{
switch (nType) {
case ConfigFrontend::CscopeVerbose:
if (sResult == "Yes")
m_nArgs |= CscopeFrontend::VerboseOut;
break;
case ConfigFrontend::CscopeSlowPath:
if (sResult == "Yes")
m_nArgs |= CscopeFrontend::SlowPathDef;
// If we got this far, then Cscope is configured properly
m_bResult = true;
break;
}
}
void CscopeVerifier::slotFinished()
{
emit done(m_bResult, m_nArgs);
delete this;
}
#include "cscopefrontend.moc"