File indexing completed on 2025-01-19 04:23:30

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