editor/src/parserworker.cpp

863 lines
34 KiB
C++

/*******************************************
* Zira Editor
* A lightweight PHP Editor
* (C)2019 https://github.com/ziracms/editor
*******************************************/
#include "parserworker.h"
#include <QProcess>
#include <QRegularExpression>
#include <QDirIterator>
#include <QDateTime>
#include <QCoreApplication>
#include <QStandardPaths>
#include "helper.h"
#include "project.h"
#include "servers.h"
#include "git.h"
const QString ANDROID_PACK_SUBDIR = "packages";
const QString ANDROID_BIN_DIR = "bin";
const QString ANDROID_GIT_CORE_DIR = "git-core";
const QString ANDROID_GIT_TEMPLATES_DIR = "templates";
const QString ANDROID_PHP_TMP_DIR = "tmp";
const QString ANDROID_INSTALL_RESULT_LINE_TEMPLATE = "<p>%1</p>";
const QString PHP_WEBSERVER_URI = "127.0.0.1:8000";
ParserWorker::ParserWorker(Settings * settings, QObject *parent) : QObject(parent)
{
phpPath = "";
gitPath = "";
bashPath = "";
sasscPath = "";
phpcsPath = "";
phpcsStandard = QString::fromStdString(settings->get("parser_phpcs_standard"));
if (phpcsStandard.size() == 0) phpcsStandard = "PEAR";
phpcsErrorSeverity = std::stoi(settings->get("parser_phpcs_error_severity"));
if (phpcsErrorSeverity < 0) phpcsErrorSeverity = 0;
phpcsWarningSeverity = std::stoi(settings->get("parser_phpcs_warning_severity"));
if (phpcsWarningSeverity < 0) phpcsWarningSeverity = 0;
encoding = settings->get("editor_encoding");
encodingFallback = settings->get("editor_fallback_encoding");
enabled = true;
searchResultsCount = 0;
searchBreaked = false;
isBusy = false;
quickResultsCount = 0;
quickBreaked = false;
wantStop = false;
phpWebServerPid = 0;
// android pack
androidHomePath="";
androidBinFiles << "php" << "git" << "git-receive-pack" << "git-upload-archive" << "git-upload-pack" << "sassc" << "termux-elf-cleaner";
androidGitFiles << "git" << "git-remote" << "git-remote-http" << "git-remote-https" << "git-receive-pack" << "git-upload-archive" << "git-upload-pack";
androidOtherFiles << "php.ini" << "gitconfig" << "cacert.pem";
#if defined(Q_OS_ANDROID)
QStringList stddirs = QStandardPaths::standardLocations(QStandardPaths::HomeLocation);
if (stddirs.size()>0) androidHomePath = stddirs.at(0);
#endif
// php path
QString phpPathStr = QString::fromStdString(settings->get("parser_php_path"));
if (phpPathStr.size() == 0) {
QProcess process(this);
process.start("which", QStringList() << "php");
if (process.waitForFinished()) {
QByteArray result = process.readAllStandardOutput();
phpPathStr = QString(result).trimmed();
}
}
if (phpPathStr.size() > 0 && Helper::fileOrFolderExists(phpPathStr)) {
phpPath = phpPathStr;
}
// git path
QString gitPathStr = QString::fromStdString(settings->get("parser_git_path"));
if (gitPathStr.size() == 0) {
QProcess process(this);
process.start("which", QStringList() << "git");
if (process.waitForFinished()) {
QByteArray result = process.readAllStandardOutput();
gitPathStr = QString(result).trimmed();
}
}
if (gitPathStr.size() > 0 && Helper::fileOrFolderExists(gitPathStr)) {
gitPath = gitPathStr;
}
// bash path
QString bashPathStr = QString::fromStdString(settings->get("parser_bash_path"));
if (bashPathStr.size() == 0) {
QProcess process(this);
process.start("which", QStringList() << "bash");
if (process.waitForFinished()) {
QByteArray result = process.readAllStandardOutput();
bashPathStr = QString(result).trimmed();
}
}
if (bashPathStr.size() > 0 && Helper::fileOrFolderExists(bashPathStr)) {
bashPath = bashPathStr;
}
// sassc path
QString sasscPathStr = QString::fromStdString(settings->get("parser_sassc_path"));
if (sasscPathStr.size() == 0) {
QProcess process(this);
process.start("which", QStringList() << "sassc");
if (process.waitForFinished()) {
QByteArray result = process.readAllStandardOutput();
sasscPathStr = QString(result).trimmed();
}
}
if (sasscPathStr.size() > 0 && Helper::fileOrFolderExists(sasscPathStr)) {
sasscPath = sasscPathStr;
}
// phpcs path
QString phpcsPathStr = QString::fromStdString(settings->get("parser_phpcs_path"));
if (phpcsPathStr.size() == 0) {
QProcess process(this);
process.start("which", QStringList() << "phpcs");
if (process.waitForFinished()) {
QByteArray result = process.readAllStandardOutput();
phpcsPathStr = QString(result).trimmed();
}
}
if (phpcsPathStr.size() > 0 && Helper::fileOrFolderExists(phpcsPathStr)) {
phpcsPath = phpcsPathStr;
}
}
ParserWorker::~ParserWorker()
{
disable();
if (phpWebServerPid != 0) {
stopPHPWebServer();
}
}
void ParserWorker::disable()
{
enabled = false;
}
void ParserWorker::lint(int tabIndex, QString path)
{
if (phpPath.size() == 0) return; // silence
QStringList errorTexts, errorLines;
QProcess process(this);
process.start(phpPath, QStringList() << "-n" << "-l" << "-f" << path);
if (!process.waitForFinished()) return;
QByteArray result = process.readAllStandardOutput();
QString errors = QString(result).trimmed();
if (errors.size() > 0 && errors.indexOf("No syntax errors")==0) errors = "";
if (errors.size() > 0) {
QRegularExpression errReg = QRegularExpression("(.+?)[ ][i][n][ ].+?[ ][o][n][ ][l][i][n][e][ ](\\d+)");
QRegularExpressionMatch errMatch;
int offset = 0;
do {
errMatch = errReg.match(errors, offset);
if (errMatch.capturedStart() >= 0) {
errorTexts.append(errMatch.captured(1));
errorLines.append(errMatch.captured(2));
offset = errMatch.capturedStart() + errMatch.capturedLength();
}
} while(errMatch.capturedStart() >= 0);
}
emit lintFinished(tabIndex, errorTexts, errorLines, errors);
}
void ParserWorker::execPHP(int tabIndex, QString path)
{
if (phpPath.size() == 0) {
emit message(tr("PHP executable not found."));
return;
}
QProcess process(this);
process.start(phpPath, QStringList() << "-d" << "max_execution_time=30" << "-f" << path);
if (!process.waitForFinished(60000)) return;
QString result = QString(process.readAllStandardOutput());
if (result.size() == 0) result = QString(process.readAllStandardError());
QString output = QString(result).trimmed();
emit execPHPFinished(tabIndex, output);
}
void ParserWorker::execSelection(int tabIndex, QString text)
{
if (phpPath.size() == 0) {
emit message(tr("PHP executable not found."));
return;
}
QProcess process(this);
process.start(phpPath, QStringList() << "-d" << "max_execution_time=30" << "-r" << text);
if (!process.waitForFinished(60000)) return;
QString result = QString(process.readAllStandardOutput());
if (result.size() == 0) result = QString(process.readAllStandardError());
QString output = QString(result).trimmed();
emit execPHPFinished(tabIndex, output);
}
void ParserWorker::startPHPWebServer(QString path)
{
if (phpPath.size() == 0) {
emit message(tr("PHP executable not found."));
return;
}
if (path.size() == 0 || !Helper::folderExists(path)) return;
if (phpWebServerPid != 0) return;
QProcess process(this);
process.setWorkingDirectory(path);
process.setProgram(phpPath);
process.setArguments(QStringList() << "-S" << PHP_WEBSERVER_URI);
process.setStandardOutputFile(QProcess::nullDevice());
process.setStandardErrorFile(QProcess::nullDevice());
if (process.startDetached(&phpWebServerPid)) {
emit execPHPWebServerFinished(true, tr("PHP web-server started."));
} else {
emit execPHPWebServerFinished(false, tr("Could not start PHP web-server."));
}
}
void ParserWorker::stopPHPWebServer()
{
if (phpWebServerPid == 0) return;
if (QProcess::startDetached("kill", {QString::number(phpWebServerPid)})) {
phpWebServerPid = 0;
// success = false (do not want browser to be opened)
emit execPHPWebServerFinished(false, tr("PHP web-server stopped."));
} else {
emit execPHPWebServerFinished(false, tr("Could not stop PHP web-server."));
}
}
void ParserWorker::phpcs(int tabIndex, QString path)
{
if (phpcsPath.size() == 0) return; //silence
QStringList errorTexts, errorLines;
QProcess process(this);
process.start(phpcsPath, QStringList() << "--standard="+phpcsStandard << "--error-severity="+Helper::intToStr(phpcsErrorSeverity) << "--warning-severity="+Helper::intToStr(phpcsWarningSeverity) << "--report=csv" << path);
if (!process.waitForFinished()) return;
QByteArray result = process.readAllStandardOutput();
QString errors = QString(result).trimmed();
if (errors.indexOf("ERROR:")==0) return;
if (errors.size() > 0) {
errors.replace("\"\"", "'");
QRegularExpression errReg = QRegularExpression("[\"](.+?)[\"]");
QRegularExpressionMatch errMatch;
int offset = 0;
do {
errMatch = errReg.match(errors, offset);
if (errMatch.capturedStart() >= 0) {
errors.replace(errMatch.capturedStart(), errMatch.capturedLength(), errMatch.captured(1).replace(",",";"));
offset = errMatch.capturedStart() + errMatch.capturedLength() - 2;
}
} while(errMatch.capturedStart() >= 0);
QStringList errorsList = errors.split("\n");
if (errorsList.size() > 1) {
int lineIndex = -1, messageIndex = -1;
QString errorsListHeader = errorsList.at(0);
QStringList errorsListHeaderList = errorsListHeader.split(",");
for (int i=0; i<errorsListHeaderList.size(); i++) {
if (errorsListHeaderList.at(i) == "Line") lineIndex = i;
//if (errorsListHeaderList.at(i) == "Column") columnIndex = i;
if (errorsListHeaderList.at(i) == "Message") messageIndex = i;
//if (errorsListHeaderList.at(i) == "Severity") severityIndex = i;
}
for (int i=1; i<errorsList.size(); i++) {
QString errorsListLine = errorsList.at(i);
QStringList errorsListLineList = errorsListLine.split(",");
if (lineIndex >= 0 && lineIndex < errorsListLineList.size()) errorLines.append(errorsListLineList.at(lineIndex));
if (messageIndex >= 0 && messageIndex < errorsListLineList.size()) errorTexts.append(errorsListLineList.at(messageIndex));
}
}
}
emit phpcsFinished(tabIndex, errorTexts, errorLines);
}
void ParserWorker::parseMixed(int tabIndex, QString text)
{
ParsePHP parser;
ParsePHP::ParseResult result = parser.parse(text);
emit parseMixedFinished(tabIndex, result);
}
void ParserWorker::parseJS(int tabIndex, QString text)
{
ParseJS parser;
ParseJS::ParseResult result = parser.parse(text);
emit parseJSFinished(tabIndex, result);
}
void ParserWorker::parseCSS(int tabIndex, QString text)
{
ParseCSS parser;
ParseCSS::ParseResult result = parser.parse(text);
emit parseCSSFinished(tabIndex, result);
}
void ParserWorker::parseProject(QString path)
{
if (isBusy) {
emit message(tr("Worker is busy. Please wait..."));
return;
}
isBusy = true;
wantStop = false;
QStringList files;
emit activateProgress();
emit activateProgressInfo(tr("Scanning")+": "+path);
parseProjectDir(path, files);
QVariantMap map = Project::createPHPResultMap();
QVariantMap data = Project::loadPHPDataMap(path);
Project::checkParsePHPFilesModified(files, data, map);
bool isBreaked = false;
bool isModified = files.count() > 0;
for (int i=0; i<files.size(); i++) {
if (!enabled || wantStop) {
isBreaked = true;
break;
}
QString file = files.at(i);
parseProjectFile(file, map);
int v = (i + 1) * 100 / files.size();
emit parseProjectProgress(v);
}
if (!isBreaked && isModified) {
emit updateProgressInfo(tr("Updating project")+"...");
Project::savePHPResults(path, map);
}
map.clear();
data.clear();
files.clear();
emit parseProjectFinished(!isBreaked, isModified);
emit deactivateProgress();
emit deactivateProgressInfo();
isBusy = false;
wantStop = false;
}
void ParserWorker::parseProjectDir(QString dir, QStringList & files)
{
QDirIterator it(dir, QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot);
while (it.hasNext()) {
QString path = it.next();
QFileInfo fInfo(path);
if (!fInfo.exists() || !fInfo.isReadable()) continue;
if (fInfo.isDir()) {
parseProjectDir(path, files);
} else if (fInfo.isFile() && Project::isProjectFile(path)) {
files.append(path);
}
}
}
void ParserWorker::parseProjectFile(QString file, QVariantMap & map)
{
QCoreApplication::processEvents();
if (wantStop) return;
if (!Helper::fileExists(file)) return;
emit updateProgressInfo(tr("Scanning")+": "+file);
QString content = Helper::loadTextFile(file, encoding, encodingFallback, true);
ParsePHP parser;
ParsePHP::ParseResult result = parser.parse(content);
QFileInfo fInfo(file);
QDateTime dt = fInfo.lastModified();
QString dts = QString::number(dt.toMSecsSinceEpoch());
Project::parsePHPResult(result, map, file, dts);
}
void ParserWorker::searchInFiles(QString searchDirectory, QString searchText, QString searchExtensions, bool searchOptionCase, bool searchOptionWord, bool searchOptionRegexp, QStringList excludeDirs)
{
if (isBusy) {
emit message(tr("Worker is busy. Please wait..."));
return;
}
isBusy = true;
wantStop = false;
if (!Helper::folderExists(searchDirectory) || searchText.size() == 0) return;
emit activateProgress();
emit activateProgressInfo(tr("Searching in")+": "+searchDirectory);
QString allowedExtensions = "";
QStringList searchExtensionsList = searchExtensions.split(",");
for (int i=0; i<searchExtensionsList.size(); i++) {
QString searchExtension = searchExtensionsList.at(i);
searchExtension = searchExtension.trimmed();
if (searchExtension.size() > 0 && searchExtension[0] == "*") searchExtension = searchExtension.mid(1);
if (searchExtension == ".*") continue;
if (searchExtension.size() > 1 && searchExtension[0] == "." && allowedExtensions.indexOf("*"+searchExtension+";") < 0) {
allowedExtensions += "*"+searchExtension+";";
}
}
searchResultsCount = 0;
searchBreaked = false;
searchInDir(searchDirectory, searchText, allowedExtensions, searchOptionCase, searchOptionWord, searchOptionRegexp, excludeDirs);
emit searchInFilesFinished();
emit deactivateProgress();
emit deactivateProgressInfo();
isBusy = false;
wantStop = false;
}
void ParserWorker::searchInDir(QString searchDirectory, QString searchText, QString searchExtensions, bool searchOptionCase, bool searchOptionWord, bool searchOptionRegexp, QStringList excludeDirs)
{
QDirIterator it(searchDirectory, QDir::AllEntries | QDir::Hidden | QDir::System | QDir::NoDotAndDotDot);
while (it.hasNext()) {
if (!enabled) break;
if (searchBreaked || wantStop) break;
QString path = it.next();
QFileInfo fInfo(path);
if (!fInfo.exists() || !fInfo.isReadable()) continue;
if (fInfo.isDir() && (fInfo.fileName() == ".git" || fInfo.fileName() == PROJECT_SUBDIR || fInfo.fileName() == ".idea" || fInfo.fileName() == ".vscode" || fInfo.fileName() == "nbproject")) continue;
if (fInfo.isDir() && excludeDirs.contains(fInfo.absoluteFilePath())) continue;
if (fInfo.isDir()) {
searchInDir(path, searchText, searchExtensions, searchOptionCase, searchOptionWord, searchOptionRegexp, excludeDirs);
} else if (fInfo.isFile()) {
if (searchExtensions.size() > 0) {
int p = path.lastIndexOf(".");
if (p < 0) continue;
QString ext = path.mid(p);
if (ext.size() == 0) continue;
if (searchExtensions.indexOf("*"+ext+";") < 0) continue;
}
searchInFile(path, searchText, searchOptionCase, searchOptionWord, searchOptionRegexp);
}
}
}
void ParserWorker::searchInFile(QString file, QString searchText, bool searchOptionCase, bool searchOptionWord, bool searchOptionRegexp)
{
QCoreApplication::processEvents();
if (wantStop) return;
if (!Helper::fileExists(file) || searchText.size() == 0) return;
emit updateProgressInfo(tr("Searching in")+": "+file);
QString content = Helper::loadTextFile(file, encoding, encodingFallback, true);
Parse parser;
if (!searchOptionWord && !searchOptionRegexp) {
// regular search
int p = -1, offset = 0;
Qt::CaseSensitivity cs = searchOptionCase ? Qt::CaseSensitive : Qt::CaseInsensitive;
do {
if (searchBreaked || wantStop) break;
p = content.indexOf(searchText, offset, cs);
if (p >= 0) {
offset = p + searchText.size();
int line = parser.getLine(content, p);
QString lineText = parser.getLineText(content, p);
searchInFilesResultFound(file, lineText, line, p);
}
} while (p >= 0);
} else if (searchOptionWord) {
// search whole words
int p = -1, offset = 0;
QRegularExpression::PatternOptions opt = QRegularExpression::NoPatternOption;
if (!searchOptionCase) opt = QRegularExpression::CaseInsensitiveOption;
QRegularExpression regexp = QRegularExpression("\\b"+QRegularExpression::escape(searchText)+"\\b", opt);
if (!regexp.isValid()) return;
do {
if (searchBreaked || wantStop) break;
QRegularExpressionMatch match = regexp.match(content, offset);
p = match.capturedStart();
if (p >= 0) {
offset = p + match.capturedLength();
int line = parser.getLine(content, p);
QString lineText = parser.getLineText(content, p);
searchInFilesResultFound(file, lineText, line, p);
}
} while (p >= 0);
} else if (searchOptionRegexp) {
// search regexp
int p = -1, offset = 0;
QRegularExpression::PatternOptions opt = QRegularExpression::NoPatternOption;
if (!searchOptionCase) opt = QRegularExpression::CaseInsensitiveOption;
QRegularExpression regexp = QRegularExpression(searchText, opt);
if (!regexp.isValid()) return;
do {
if (searchBreaked || wantStop) break;
QRegularExpressionMatch match = regexp.match(content, offset);
p = match.capturedStart();
if (p >= 0) {
offset = p + match.capturedLength();
int line = parser.getLine(content, p);
QString lineText = parser.getLineText(content, p);
searchInFilesResultFound(file, lineText, line, p);
}
} while (p >= 0);
}
}
void ParserWorker::searchInFilesResultFound(QString file, QString lineText, int line, int symbol)
{
searchResultsCount++;
emit searchInFilesFound(file, lineText, line, symbol);
if (searchResultsCount >= 1000) {
searchBreaked = true;
emit searchInFilesFound("", tr("Too many results. Search stopped."), -1, -1);
}
}
void ParserWorker::gitCommand(QString path, QString command, QStringList attrs, bool outputResult, bool silent)
{
if (gitPath.size() == 0) {
if (!silent) emit message(tr("Git not found."));
return;
}
if (path.size() == 0 || !Helper::folderExists(path)) return;
bool useProgress = false;
if (command == GIT_PUSH_COMMAND || command == GIT_PULL_COMMAND || command == GIT_CLONE_COMMAND) useProgress = true;
if (useProgress && !isBusy) emit activateProgress();
QProcess process(this);
process.setWorkingDirectory(path);
process.start(gitPath, QStringList() << command << attrs);
if (!process.waitForFinished(300000)) {
if (useProgress && !isBusy) emit deactivateProgress();
return;
}
QString result = QString(process.readAllStandardOutput());
if (result.size() == 0) result = QString(process.readAllStandardError());
emit gitCommandFinished(command, result, outputResult);
if (useProgress && !isBusy) emit deactivateProgress();
}
void ParserWorker::serversCommand(QString command, QString pwd)
{
if (bashPath.size() == 0) {
emit message(tr("Bash not found."));
return;
}
if (command.size() == 0) return;
if (!isBusy) emit activateProgress();
QString errorApache = "";
// apache2
QProcess processApache(this);
processApache.start(bashPath, QStringList() << "-c" << Servers::generateApacheServiceCommand(command, pwd));
if (!processApache.waitForFinished(300000)) {
if (!isBusy) emit deactivateProgress();
return;
}
QString outputApache = QString(processApache.readAllStandardOutput());
if (outputApache.size() == 0) errorApache = QString(processApache.readAllStandardError());
if (errorApache.size() > 0) {
QStringList errorApacheList = errorApache.split("\n");
errorApache = "";
for (int i=0; i<errorApacheList.size(); i++) {
QString error = errorApacheList.at(i).trimmed();
if (error == "[sudo] password for root:") continue;
if (error == "sudo: 1 incorrect password attempt") continue;
if (error.indexOf("[sudo] password for root:") == 0) error = error.mid(25).trimmed();
if (error.size() == 0) continue;
if (errorApache.size() > 0) errorApache += "\n";
errorApache += error;
}
}
if (errorApache.size() > 0) {
emit serversCommandFinished(errorApache.trimmed());
if (!isBusy) emit deactivateProgress();
return;
}
// mariadb
QString errorMariadb = "";
QProcess processMariadb(this);
processMariadb.start(bashPath, QStringList() << "-c" << Servers::generateMariaDBServiceCommand(command, pwd));
if (!processMariadb.waitForFinished(300000)) {
if (!isBusy) emit deactivateProgress();
return;
}
QString outputMariadb = QString(processMariadb.readAllStandardOutput());
if (outputMariadb.size() == 0) errorMariadb = QString(processMariadb.readAllStandardError());
if (errorMariadb.size() > 0) {
QStringList outputMariadbList = errorMariadb.split("\n");
errorMariadb = "";
for (int i=0; i<outputMariadbList.size(); i++) {
QString error = outputMariadbList.at(i).trimmed();
if (error == "[sudo] password for root:") continue;
if (error == "sudo: 1 incorrect password attempt") continue;
if (error.indexOf("[sudo] password for root:") == 0) error = error.mid(25).trimmed();
if (error.size() == 0) continue;
if (errorMariadb.size() > 0) errorMariadb += "\n";
errorMariadb += error;
}
}
if (errorMariadb.size() > 0) {
emit serversCommandFinished(outputApache.trimmed() + "\n\n" + errorMariadb.trimmed());
if (!isBusy) emit deactivateProgress();
return;
}
if (command != SERVERS_STATUS_CMD) {
//if (!isBusy) emit deactivateProgress();
serversCommand(SERVERS_STATUS_CMD, pwd);
return;
}
emit serversCommandFinished(outputApache.trimmed() + "\n\n" + outputMariadb.trimmed());
if (!isBusy) emit deactivateProgress();
}
void ParserWorker::sassCommand(QString src, QString dst)
{
if (sasscPath.size() == 0) {
emit message(tr("Sassc not found."));
return;
}
if (src.size() == 0 || dst.size() == 0) return;
if (!isBusy) emit activateProgress();
QProcess process(this);
process.start(sasscPath, QStringList() << src << dst);
if (!process.waitForFinished()) {
if (!isBusy) emit deactivateProgress();
return;
}
QString error = QString(process.readAllStandardError());
emit sassCommandFinished(error);
if (!isBusy) emit deactivateProgress();
}
void ParserWorker::quickFind(QString dir, QString text, WordsMapList words, QStringList wordPrefixes)
{
if (!isBusy) emit activateProgress();
quickResultsCount = 0;
quickBreaked = false;
// words
int it = 0;
for (auto wordsList : words) {
//QCoreApplication::processEvents();
if (!enabled) break;
QString prefix = "";
if (it < wordPrefixes.size()) prefix = wordPrefixes.at(it);
it++;
int co = 0;
// words
for (auto it : wordsList) {
QString name = QString::fromStdString(it.first);
if (name.indexOf(text, 0, Qt::CaseInsensitive) >= 0) {
QString dec_str = QString::fromStdString(it.second);
int p = dec_str.lastIndexOf(":");
if (p >= 0) {
QString path = dec_str.mid(0, p).trimmed();
int line = dec_str.mid(p+1).toInt();
emit quickFound(text, prefix+name, path, line);
co++;
if (co >= 100) break;
}
}
}
}
// search files
quickFindInDir(dir, dir, text);
if (!isBusy) emit deactivateProgress();
}
void ParserWorker::quickFindInDir(QString startDir, QString dir, QString text)
{
QString prefix = "file: ";
QDirIterator it(dir, QDir::AllEntries | QDir::Hidden | QDir::System | QDir::NoDotAndDotDot);
while (it.hasNext()) {
//QCoreApplication::processEvents();
if (!enabled) break;
if (quickBreaked) break;
QString path = it.next();
QFileInfo fInfo(path);
if (!fInfo.exists() || !fInfo.isReadable()) continue;
if (fInfo.isDir() && (fInfo.fileName() == ".git" || fInfo.fileName() == PROJECT_SUBDIR || fInfo.fileName() == ".idea" || fInfo.fileName() == ".vscode" || fInfo.fileName() == "nbproject")) continue;
if (fInfo.isDir()) {
quickFindInDir(startDir, path, text);
} else if (fInfo.isFile()) {
if (fInfo.fileName().indexOf(text, 0, Qt::CaseInsensitive) >= 0) {
QString info = path;
if (info.size() > startDir.size()) info = info.mid(startDir.size()+1);
emit quickFound(text, prefix+info, path, 1);
quickResultsCount++;
if (quickResultsCount >= 100) {
quickBreaked = true;
break;
}
}
}
}
}
void ParserWorker::cancelRequested()
{
wantStop = true;
}
bool ParserWorker::createAndroidDirectory(QDir rootDir, QString path)
{
if (!rootDir.mkpath(path)) return false;
if (!setAndroidFilePermissions(path)) return false;
return true;
}
bool ParserWorker::setAndroidFilePermissions(QFile &f)
{
return f.setPermissions(
QFileDevice::ReadOwner |
QFileDevice::ReadGroup |
QFileDevice::ReadOther |
QFileDevice::WriteOwner |
QFileDevice::WriteGroup |
QFileDevice::WriteOther |
QFileDevice::ExeOwner |
QFileDevice::ExeGroup |
QFileDevice::ExeOther
);
}
bool ParserWorker::setAndroidFilePermissions(QString path)
{
QFile f(path);
return setAndroidFilePermissions(f);
}
bool ParserWorker::installAndroidFile(QString fileName, QString installDir)
{
QFile f("assets:/"+fileName);
if (!f.exists()) return false;
QFile fi(installDir+"/"+fileName);
if (fi.exists()) fi.remove();
if (!f.copy(installDir+"/"+fileName)) return false;
QFile pf(installDir+"/"+fileName);
if (!setAndroidFilePermissions(pf)) return false;
return true;
}
void ParserWorker::installAndroidPack()
{
if (androidHomePath.size() == 0) return;
if (isAndroidPackInstalled()) {
setAndroidBinPaths();
return;
}
emit installAndroidPackFinished(ANDROID_INSTALL_RESULT_LINE_TEMPLATE.arg(tr("Installing development pack...")));
if (installAndroidPackFiles()) {
QString androidPackInstallPath = androidHomePath + "/" + ANDROID_PACK_SUBDIR;
QString result = "";
QProcess phpProcess(this);
phpProcess.start(androidPackInstallPath+"/"+ANDROID_BIN_DIR+"/php", QStringList() << "-v");
if (phpProcess.waitForFinished()) {
QByteArray phpResult = phpProcess.readAllStandardError();
phpResult += phpProcess.readAllStandardOutput();
result += ANDROID_INSTALL_RESULT_LINE_TEMPLATE.arg(QString(phpResult).trimmed().split("\n").at(0));
}
QProcess gitProcess(this);
gitProcess.start(androidPackInstallPath+"/"+ANDROID_BIN_DIR+"/git", QStringList() << "--version");
if (gitProcess.waitForFinished()) {
QByteArray gitResult = gitProcess.readAllStandardError();
gitResult += gitProcess.readAllStandardOutput();
result += ANDROID_INSTALL_RESULT_LINE_TEMPLATE.arg(QString(gitResult).trimmed().split("\n").at(0));
}
QProcess sasscProcess(this);
sasscProcess.start(androidPackInstallPath+"/"+ANDROID_BIN_DIR+"/sassc", QStringList() << "--version");
if (sasscProcess.waitForFinished()) {
QByteArray sasscResult = sasscProcess.readAllStandardError();
sasscResult += sasscProcess.readAllStandardOutput();
result += ANDROID_INSTALL_RESULT_LINE_TEMPLATE.arg(QString(sasscResult).trimmed().split("\n").at(0));
}
setAndroidBinPaths();
emit installAndroidPackFinished(ANDROID_INSTALL_RESULT_LINE_TEMPLATE.arg(tr("Development pack successfully installed."))+result);
} else {
emit installAndroidPackFinished(ANDROID_INSTALL_RESULT_LINE_TEMPLATE.arg(tr("Installation failed :(")));
}
}
bool ParserWorker::installAndroidPackFiles()
{
// creating directories
QDir androidHomeDir(androidHomePath);
QFileInfo androidHomeDirInfo(androidHomePath);
if (!androidHomeDirInfo.isWritable() || !androidHomeDirInfo.isExecutable()) return false;
if (!createAndroidDirectory(androidHomeDir, androidHomePath + "/" + ANDROID_PACK_SUBDIR)) return false;
QString androidPackInstallPath = androidHomePath + "/" + ANDROID_PACK_SUBDIR;
QDir d(androidPackInstallPath);
if (!createAndroidDirectory(d, androidPackInstallPath+"/"+ANDROID_BIN_DIR)) return false;
if (!createAndroidDirectory(d, androidPackInstallPath+"/"+ANDROID_BIN_DIR+"/"+ANDROID_GIT_CORE_DIR)) return false;
if (!createAndroidDirectory(d, androidPackInstallPath+"/"+ANDROID_GIT_TEMPLATES_DIR)) return false;
if (!createAndroidDirectory(d, androidPackInstallPath+"/"+ANDROID_PHP_TMP_DIR)) return false;
// copying files
for (QString fileName : androidBinFiles) {
if (!installAndroidFile(fileName, androidPackInstallPath+"/"+ANDROID_BIN_DIR)) return false;
}
for (QString fileName : androidGitFiles) {
if (!installAndroidFile(fileName, androidPackInstallPath+"/"+ANDROID_BIN_DIR+"/"+ANDROID_GIT_CORE_DIR)) return false;
}
for (QString fileName : androidOtherFiles) {
if (!installAndroidFile(fileName, androidPackInstallPath)) return false;
}
// .gitconfig
QFile gitConfigHidden(androidPackInstallPath+"/.gitconfig");
if (gitConfigHidden.exists()) gitConfigHidden.remove();
QFile gitConfig(androidPackInstallPath+"/gitconfig");
if (gitConfig.exists()) gitConfig.rename(androidPackInstallPath+"/.gitconfig");
// run termux-elf-cleaner
QStringList cleanFiles;
for (QString fileName : androidBinFiles) {
if (fileName == "termux-elf-cleaner") continue;
cleanFiles << androidPackInstallPath+"/"+ANDROID_BIN_DIR+"/"+fileName;
}
for (QString fileName : androidGitFiles) {
cleanFiles << androidPackInstallPath+"/"+ANDROID_BIN_DIR+"/"+ANDROID_GIT_CORE_DIR+"/"+fileName;
}
QProcess cleanProcess(this);
cleanProcess.start(androidPackInstallPath+"/"+ANDROID_BIN_DIR+"/termux-elf-cleaner", cleanFiles);
if (!cleanProcess.waitForFinished()) return false;
return true;
}
bool ParserWorker::isAndroidPackInstalled()
{
if (androidHomePath.size() == 0) return false;
for (QString fileName : androidBinFiles) {
QFileInfo fileInfo(androidHomePath+"/"+ANDROID_PACK_SUBDIR+"/"+ANDROID_BIN_DIR+"/"+fileName);
if (!fileInfo.exists() || !fileInfo.isExecutable()) return false;
}
for (QString fileName : androidGitFiles) {
QFileInfo fileInfo(androidHomePath+"/"+ANDROID_PACK_SUBDIR+"/"+ANDROID_BIN_DIR+"/"+ANDROID_GIT_CORE_DIR+"/"+fileName);
if (!fileInfo.exists() || !fileInfo.isExecutable()) return false;
}
for (QString fileName : androidOtherFiles) {
if (fileName == "gitconfig") fileName = ".gitconfig";
QFileInfo fileInfo(androidHomePath+"/"+ANDROID_PACK_SUBDIR+"/"+fileName);
if (!fileInfo.exists() || !fileInfo.isReadable()) return false;
}
return true;
}
void ParserWorker::setAndroidBinPaths()
{
if (androidHomePath.size() == 0) return;
if (phpPath.size() == 0) {
QString phpPathStr = androidHomePath+"/"+ANDROID_PACK_SUBDIR+"/"+ANDROID_BIN_DIR+"/php";
if (Helper::fileOrFolderExists(phpPathStr)) phpPath = phpPathStr;
}
if (gitPath.size() == 0) {
QString gitPathStr = androidHomePath+"/"+ANDROID_PACK_SUBDIR+"/"+ANDROID_BIN_DIR+"/git";
if (Helper::fileOrFolderExists(gitPathStr)) gitPath = gitPathStr;
}
if (sasscPath.size() == 0) {
QString sasscPathStr = androidHomePath+"/"+ANDROID_PACK_SUBDIR+"/"+ANDROID_BIN_DIR+"/sassc";
if (Helper::fileOrFolderExists(sasscPathStr)) sasscPath = sasscPathStr;
}
}