File indexing completed on 2024-04-21 09:42:26

0001 /*
0002     SPDX-FileCopyrightText: 2007-2008 Robert Knight <robertknight@gmail.com>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 // Own
0008 #include "ShellCommand.h"
0009 
0010 // KDE
0011 #include <KShell>
0012 
0013 using Konsole::ShellCommand;
0014 
0015 ShellCommand::ShellCommand(const QString &aCommand)
0016     : _arguments(KShell::splitArgs(aCommand))
0017 {
0018 }
0019 
0020 ShellCommand::ShellCommand(const QString &aCommand, const QStringList &aArguments)
0021     : _arguments(aArguments)
0022 {
0023     if (!_arguments.isEmpty()) {
0024         _arguments[0] = aCommand;
0025     }
0026 }
0027 
0028 QString ShellCommand::fullCommand() const
0029 {
0030     QStringList quotedArgs(_arguments);
0031     for (int i = 0; i < quotedArgs.count(); i++) {
0032         QString arg = quotedArgs.at(i);
0033         bool hasSpace = false;
0034         for (int j = 0; j < arg.length(); j++) {
0035             if (arg[j].isSpace()) {
0036                 hasSpace = true;
0037             }
0038         }
0039         if (hasSpace) {
0040             quotedArgs[i] = QLatin1Char('\"') + arg + QLatin1Char('\"');
0041         }
0042     }
0043     return quotedArgs.join(QLatin1Char(' '));
0044 }
0045 
0046 QString ShellCommand::command() const
0047 {
0048     if (!_arguments.isEmpty()) {
0049         return _arguments[0];
0050     }
0051     return QString();
0052 }
0053 
0054 QStringList ShellCommand::arguments() const
0055 {
0056     return _arguments;
0057 }
0058 
0059 QStringList ShellCommand::expand(const QStringList &items)
0060 {
0061     QStringList result;
0062     result.reserve(items.size());
0063 
0064     for (const QString &item : items) {
0065         result << expand(item);
0066     }
0067 
0068     return result;
0069 }
0070 
0071 QString ShellCommand::expand(const QString &text)
0072 {
0073     QString result = text;
0074     expandEnv(result);
0075     return result;
0076 }
0077 
0078 bool ShellCommand::isValidEnvCharacter(const QChar &ch)
0079 {
0080     const ushort code = ch.unicode();
0081     return isValidLeadingEnvCharacter(ch) || ('0' <= code && code <= '9');
0082 }
0083 
0084 bool ShellCommand::isValidLeadingEnvCharacter(const QChar &ch)
0085 {
0086     const ushort code = ch.unicode();
0087     return (code == '_') || ('A' <= code && code <= 'Z');
0088 }
0089 
0090 /*
0091  * expandEnv
0092  *
0093  * Expand environment variables in text. Escaped '$' characters are ignored.
0094  * Return true if any variables were expanded
0095  */
0096 bool ShellCommand::expandEnv(QString &text)
0097 {
0098     const QLatin1Char dollarChar('$');
0099     const QLatin1Char backslashChar('\\');
0100 
0101     int dollarPos = 0;
0102     bool expanded = false;
0103 
0104     // find and expand all environment variables beginning with '$'
0105     while ((dollarPos = text.indexOf(dollarChar, dollarPos)) != -1) {
0106         // if '$' is the last character, there is no way of expanding
0107         if (dollarPos == text.length() - 1) {
0108             break;
0109         }
0110 
0111         // skip escaped '$'
0112         if (dollarPos > 0 && text.at(dollarPos - 1) == backslashChar) {
0113             dollarPos++;
0114             continue;
0115         }
0116 
0117         // if '$' is followed by an invalid leading character, skip this '$'
0118         if (!isValidLeadingEnvCharacter(text.at(dollarPos + 1))) {
0119             dollarPos++;
0120             continue;
0121         }
0122 
0123         int endPos = dollarPos + 1;
0124         Q_ASSERT(endPos < text.length());
0125         while (endPos < text.length() && isValidEnvCharacter(text.at(endPos))) {
0126             endPos++;
0127         }
0128 
0129         const int len = endPos - dollarPos;
0130         const QString key = text.mid(dollarPos + 1, len - 1);
0131         const QString value = QString::fromLocal8Bit(qgetenv(key.toLocal8Bit().constData()));
0132 
0133         if (!value.isEmpty()) {
0134             text.replace(dollarPos, len, value);
0135             expanded = true;
0136             dollarPos = dollarPos + value.length();
0137         } else {
0138             dollarPos = endPos;
0139         }
0140     }
0141 
0142     return expanded;
0143 }