File indexing completed on 2024-04-28 17:06:55

0001 /*
0002  * Copyright 2010-2012 Bart Kroon <bart@tarmack.eu>
0003  * 
0004  * Redistribution and use in source and binary forms, with or without
0005  * modification, are permitted provided that the following conditions
0006  * are met:
0007  * 
0008  * 1. Redistributions of source code must retain the above copyright
0009  *   notice, this list of conditions and the following disclaimer.
0010  * 2. Redistributions in binary form must reproduce the above copyright
0011  *   notice, this list of conditions and the following disclaimer in the
0012  *   documentation and/or other materials provided with the distribution.
0013  * 
0014  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
0015  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
0016  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
0017  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
0018  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
0019  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
0020  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
0021  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
0022  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
0023  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0024  */
0025 
0026 #include "Shell.h"
0027 
0028 #include <stdlib.h>
0029 #include <QDBusInterface>
0030 #include <QDir>
0031 #include <klocalizedstring.h>
0032 #include <QDebug>
0033 #include <QProcess>
0034 #include <QDateTime>
0035 #include <QFileSystemWatcher>
0036 
0037 void Shell::walkDir(QString path)
0038 {
0039     QDir dir = QDir(QFileInfo(path).canonicalFilePath());
0040     QFileInfoList list = dir.entryInfoList(QStringList(), QDir::NoDotAndDotDot | QDir::Files);
0041 
0042     for (const QFileInfo &file : list) {
0043         const QString canonicalPath = file.canonicalFilePath();
0044 
0045         if (file.isFile() && file.isExecutable()) {
0046             m_index[file.fileName()] = canonicalPath;
0047             m_modified[file.fileName()] = file.lastModified().toSecsSinceEpoch();
0048         }
0049     }
0050 }
0051 
0052 namespace
0053 {
0054 QStringList parsePathEnv()
0055 {
0056     QString pathEnv = getenv("PATH");
0057 #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
0058     QStringList pathList = pathEnv.split(":", QString::SkipEmptyParts);
0059 #else
0060     QStringList pathList = pathEnv.split(":", Qt::SkipEmptyParts);
0061 #endif
0062     QStringList absPathList;
0063     for (const QString &path : pathList) {
0064         const QString canonicalPath = QFileInfo(path).canonicalFilePath();
0065         if (canonicalPath.isEmpty()) {
0066             continue;
0067         }
0068         absPathList.append(canonicalPath);
0069     }
0070 
0071     absPathList.removeDuplicates();
0072     return absPathList;
0073 }
0074 }
0075 
0076 Shell::Shell(QObject *parent) :
0077     Provider(parent),
0078     m_fsWatcher(new QFileSystemWatcher(this))
0079 {
0080     connect(m_fsWatcher, &QFileSystemWatcher::directoryChanged, this, &Shell::onDirectoryChanged);
0081 
0082     for (const QString &dir : parsePathEnv()) {
0083         m_fsWatcher->addPath(dir);
0084         walkDir(dir);
0085     }
0086 }
0087 
0088 Shell::~Shell()
0089 {}
0090 
0091 QList<ProviderResult *> Shell::getResults(QString query)
0092 {
0093     QList<ProviderResult*> list;
0094 
0095     QString command;
0096     QString args;
0097 
0098     if (query.contains(' ')) {
0099         command = query.left(query.indexOf(" "));
0100         args = query.right(query.length() - query.indexOf(" "));
0101     } else {
0102         command = query;
0103     }
0104 
0105     const qint64 currentSecsSinceEpoch = QDateTime::currentSecsSinceEpoch();
0106     QHashIterator<QString, QString> iterator(this->m_index);
0107     while (iterator.hasNext()) {
0108         iterator.next();
0109 
0110         if (!iterator.key().startsWith(command, Qt::CaseInsensitive)) {
0111             continue;
0112         }
0113 
0114         ProviderResult *app = new ProviderResult;
0115         app->name = iterator.value() + args;
0116         app->completion = iterator.key();
0117         app->icon = "system-run";
0118         app->object = this;
0119         app->program = iterator.value() + args;
0120         app->type = i18n("Shell command");
0121 
0122         app->priority = currentSecsSinceEpoch - m_modified[iterator.key()];
0123 
0124         list.append(app);
0125     }
0126     return list;
0127 }
0128 
0129 int Shell::launch(const QString &exec)
0130 {
0131 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
0132     QStringList args = exec.split(" ");
0133 #else
0134     QStringList args = QProcess::splitCommand(exec);
0135 #endif
0136     if (args.isEmpty()) {
0137         qWarning() << "Asked to launch invalid program:" << exec;
0138         return 0;
0139     }
0140     QString program(args.takeFirst());
0141     QProcess::startDetached(program, args);
0142     return 0;
0143 }
0144 
0145 void Shell::onDirectoryChanged(const QString &path)
0146 {
0147     // First remove the old
0148     QStringList toRemove;
0149     for (const QString &fullProgramPath : m_index.values()) {
0150         if (!fullProgramPath.startsWith(path)) {
0151             continue;
0152         }
0153 
0154         QFileInfo programInfo(fullProgramPath);
0155         if (programInfo.dir().path() == path) {
0156             toRemove.append(programInfo.fileName());
0157         }
0158     }
0159 
0160     // Too lazy to use an iterator
0161     for (const QString &program : toRemove) {
0162         m_index.remove(program);
0163         m_modified.remove(program);
0164     }
0165 
0166     // Then re-index the path
0167     walkDir(path);
0168 }
0169 
0170 
0171 // kate: indent-mode cstyle; space-indent on; indent-width 4;