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.
525 lines
13 KiB
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 <qfileinfo.h>
|
|
#include <qtimer.h>
|
|
#include <kconfig.h>
|
|
#include <kmessagebox.h>
|
|
#include <klocale.h>
|
|
#include <kglobalsettings.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"
|
|
|
|
QString 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 QStringList& slArgs)
|
|
{
|
|
QStringList 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 QString& sText, bool bCase,
|
|
uint nMaxRecords)
|
|
{
|
|
QString sQuery;
|
|
QStringList slArgs;
|
|
|
|
m_nMaxRecords = nMaxRecords;
|
|
|
|
// Create the Cscope command line
|
|
slArgs.append(QString("-L") + QString::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()
|
|
{
|
|
QStringList 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 QString& 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(QString& 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 QString& 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(QWidget* pMainWidget) : QObject(),
|
|
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 QLabel(i18n("Processing query results, "
|
|
"please wait..."), m_pMainWidget);
|
|
m_pLabel->setFrameStyle(QFrame::Box | QFrame::Plain);
|
|
m_pLabel->setLineWidth(1);
|
|
m_pLabel->adjustSize();
|
|
|
|
m_pLabel->setPaletteBackgroundColor(
|
|
KGlobalSettings::highlightColor());
|
|
m_pLabel->setPaletteForegroundColor(
|
|
KGlobalSettings::highlightedTextColor());
|
|
|
|
QTimer::singleShot(1000, this, 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 QProgressBar(m_pMainWidget);
|
|
QTimer::singleShot(1000, this, 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, SIGNAL(result(uint, const QString&)), this,
|
|
SLOT(slotConfigResult(uint, const QString&)));
|
|
connect(pConf, SIGNAL(finished(uint)), this, SLOT(slotFinished()));
|
|
|
|
pConf->run(Config().getCscopePath(), "", "", true);
|
|
}
|
|
|
|
void CscopeVerifier::slotConfigResult(uint nType, const QString& 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"
|