summaryrefslogtreecommitdiffstats
path: root/src/cscopefrontend.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/cscopefrontend.cpp')
-rw-r--r--src/cscopefrontend.cpp524
1 files changed, 524 insertions, 0 deletions
diff --git a/src/cscopefrontend.cpp b/src/cscopefrontend.cpp
new file mode 100644
index 0000000..7c8f288
--- /dev/null
+++ b/src/cscopefrontend.cpp
@@ -0,0 +1,524 @@
+/***************************************************************************
+ *
+ * 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"