File indexing completed on 2024-05-05 05:30:13
0001 /* 0002 SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0003 SPDX-FileCopyrightText: 2021-2022 Harald Sitter <sitter@kde.org> 0004 */ 0005 0006 #include "CommandOutputContext.h" 0007 0008 #include <utility> 0009 0010 #include <QDebug> 0011 #include <QProcess> 0012 #include <QStandardPaths> 0013 0014 #include <KLocalizedString> 0015 #include <KOSRelease> 0016 0017 CommandOutputContext::CommandOutputContext(const QStringList &findExecutables, const QString &executable, const QStringList &arguments, QObject *parent) 0018 : QObject(parent) 0019 , m_executableName(executable) 0020 , m_executablePath(QStandardPaths::findExecutable(m_executableName)) 0021 , m_arguments(arguments) 0022 , m_bugReportUrl(KOSRelease().bugReportUrl()) 0023 { 0024 // Various utilities are installed in sbin, but work without elevated privileges 0025 if (m_executablePath.isEmpty()) { 0026 m_executablePath = 0027 QStandardPaths::findExecutable(m_executableName, {QStringLiteral("/usr/local/sbin"), QStringLiteral("/usr/sbin"), QStringLiteral("/sbin")}); 0028 } 0029 0030 m_foundExecutablePaths[executable] = m_executablePath; 0031 for (const QString &findExecutable : findExecutables) { 0032 m_foundExecutablePaths[findExecutable] = QStandardPaths::findExecutable(findExecutable); 0033 } 0034 0035 metaObject()->invokeMethod(this, &CommandOutputContext::load); 0036 } 0037 0038 CommandOutputContext::CommandOutputContext(const QString &executable, const QStringList &arguments, QObject *parent) 0039 : CommandOutputContext({/* executable is by default always searched for */}, executable, arguments, parent) 0040 { 0041 } 0042 0043 QString CommandOutputContext::executableName() const 0044 { 0045 return m_executableName; 0046 } 0047 0048 QStringList CommandOutputContext::arguments() const 0049 { 0050 return m_arguments; 0051 } 0052 0053 QString CommandOutputContext::filter() const 0054 { 0055 return m_filter; 0056 } 0057 0058 void CommandOutputContext::setFilter(const QString &filter) 0059 { 0060 m_filter = filter; 0061 if (m_filter.isEmpty()) { 0062 m_text = m_originalLines.join('\n'); 0063 } else { 0064 m_text.clear(); 0065 for (const QString &line : std::as_const(m_originalLines)) { 0066 if (line.contains(filter, Qt::CaseInsensitive)) { 0067 m_text += line + '\n'; 0068 } 0069 } 0070 } 0071 Q_EMIT textChanged(); 0072 Q_EMIT filterChanged(); 0073 } 0074 0075 void CommandOutputContext::reset() 0076 { 0077 m_ready = false; 0078 m_error.clear(); 0079 m_explanation.clear(); 0080 m_text.clear(); 0081 m_filter.clear(); 0082 Q_EMIT readyChanged(); 0083 Q_EMIT errorChanged(); 0084 Q_EMIT explanationChanged(); 0085 Q_EMIT textChanged(); 0086 Q_EMIT filterChanged(); 0087 0088 // Not exposed as properties 0089 m_originalLines.clear(); 0090 } 0091 0092 void CommandOutputContext::load() 0093 { 0094 reset(); 0095 0096 for (auto it = m_foundExecutablePaths.cbegin(); it != m_foundExecutablePaths.cend(); ++it) { 0097 if (it.value().isEmpty()) { 0098 setError(xi18nc("@info", "The <command>%1</command> tool is required to display this page, but could not be found", it.key()), 0099 xi18nc("@info", 0100 "You can search for it and install it using your package manager.<nl/>" 0101 "Then please report this packaging issue to your distribution.")); 0102 return; 0103 } 0104 } 0105 0106 auto proc = new QProcess(this); 0107 proc->setProcessChannelMode(QProcess::MergedChannels); 0108 connect(proc, &QProcess::finished, this, [this, proc](int /* exitCode */, QProcess::ExitStatus exitStatus) { 0109 proc->deleteLater(); 0110 0111 switch (exitStatus) { 0112 case QProcess::CrashExit: 0113 return setError(xi18nc("@info", "The <command>%1</command> tool crashed while generating page content", m_executableName), 0114 xi18nc("@Info", "Try again later. If keeps happening, please report the crash to your distribution.")); 0115 case QProcess::NormalExit: 0116 break; 0117 } 0118 0119 m_text = QString::fromLocal8Bit(proc->readAllStandardOutput()); 0120 if (m_trimAllowed) { 0121 m_text = m_text.trimmed(); 0122 } 0123 m_originalLines = m_text.split('\n'); 0124 if (!m_filter.isEmpty()) { 0125 // re-apply filter on new text 0126 setFilter(m_filter); 0127 } 0128 0129 Q_EMIT textChanged(); 0130 setReady(); 0131 }); 0132 proc->start(m_executablePath, m_arguments); 0133 } 0134 0135 void CommandOutputContext::setError(const QString &message, const QString &explanation = QString()) 0136 { 0137 m_error = message; 0138 0139 if (!explanation.isEmpty()) { 0140 m_explanation = explanation; 0141 } 0142 0143 Q_EMIT errorChanged(); 0144 Q_EMIT explanationChanged(); 0145 0146 setReady(); 0147 } 0148 0149 void CommandOutputContext::setReady() 0150 { 0151 m_ready = true; 0152 Q_EMIT readyChanged(); 0153 } 0154 0155 void CommandOutputContext::setTrimAllowed(bool allow) 0156 { 0157 m_trimAllowed = allow; 0158 } 0159 0160 #include "moc_CommandOutputContext.cpp"