File indexing completed on 2024-05-12 03:55:00

0001 /*
0002     This file is part of the KDE libraries
0003 
0004     SPDX-FileCopyrightText: 2003, 2007 Oswald Buddenhagen <ossi@kde.org>
0005 
0006     SPDX-License-Identifier: LGPL-2.0-or-later
0007 */
0008 
0009 #include "kshell.h"
0010 #include "kshell_p.h"
0011 
0012 #include <kuser.h>
0013 
0014 #include <QChar>
0015 #include <QStringList>
0016 
0017 static int fromHex(QChar cUnicode)
0018 {
0019     char c = cUnicode.toLatin1();
0020 
0021     if (c >= '0' && c <= '9') {
0022         return c - '0';
0023     } else if (c >= 'A' && c <= 'F') {
0024         return c - 'A' + 10;
0025     } else if (c >= 'a' && c <= 'f') {
0026         return c - 'a' + 10;
0027     }
0028     return -1;
0029 }
0030 
0031 inline static bool isQuoteMeta(QChar cUnicode)
0032 {
0033     char c = cUnicode.toLatin1();
0034     return c == '\\' || c == '\'' || c == '"' || c == '$';
0035 }
0036 
0037 inline static bool isMeta(QChar cUnicode)
0038 {
0039     static const uchar iqm[] = {0x00, 0x00, 0x00, 0x00, 0xdc, 0x07, 0x00, 0xd8, 0x00, 0x00, 0x00, 0x38, 0x01, 0x00, 0x00, 0x38}; // \'"$`<>|;&(){}*?#[]
0040 
0041     uint c = cUnicode.unicode();
0042 
0043     return (c < sizeof(iqm) * 8) && (iqm[c / 8] & (1 << (c & 7)));
0044 }
0045 
0046 QStringList KShell::splitArgs(const QString &args, Options flags, Errors *err)
0047 {
0048     QStringList ret;
0049     bool firstword = flags & AbortOnMeta;
0050 
0051     for (int pos = 0;;) {
0052         QChar c;
0053         do {
0054             if (pos >= args.length()) {
0055                 goto okret;
0056             }
0057             c = args.unicode()[pos++];
0058         } while (c == QLatin1Char(' '));
0059         QString cret;
0060         if ((flags & TildeExpand) && c == QLatin1Char('~')) {
0061             int opos = pos;
0062             for (;; pos++) {
0063                 if (pos >= args.length()) {
0064                     break;
0065                 }
0066                 c = args.unicode()[pos];
0067                 if (c == QLatin1Char('/') || c == QLatin1Char(' ')) {
0068                     break;
0069                 }
0070                 if (isQuoteMeta(c)) {
0071                     pos = opos;
0072                     c = QLatin1Char('~');
0073                     goto notilde;
0074                 }
0075                 if ((flags & AbortOnMeta) && isMeta(c)) {
0076                     goto metaerr;
0077                 }
0078             }
0079             QString ccret = homeDir(args.mid(opos, pos - opos));
0080             if (ccret.isEmpty()) {
0081                 pos = opos;
0082                 c = QLatin1Char('~');
0083                 goto notilde;
0084             }
0085             if (pos >= args.length()) {
0086                 ret += ccret;
0087                 goto okret;
0088             }
0089             pos++;
0090             if (c == QLatin1Char(' ')) {
0091                 ret += ccret;
0092                 firstword = false;
0093                 continue;
0094             }
0095             cret = ccret;
0096         }
0097         // before the notilde label, as a tilde does not match anyway
0098         if (firstword) {
0099             if (c == QLatin1Char('_') //
0100                 || (c >= QLatin1Char('A') && c <= QLatin1Char('Z')) //
0101                 || (c >= QLatin1Char('a') && c <= QLatin1Char('z'))) {
0102                 int pos2 = pos;
0103                 QChar cc;
0104                 do {
0105                     if (pos2 >= args.length()) {
0106                         // Exactly one word
0107                         ret += args.mid(pos - 1);
0108                         goto okret;
0109                     }
0110                     cc = args.unicode()[pos2++];
0111                 } while (cc == QLatin1Char('_') /* clang-format off */
0112                          || (cc >= QLatin1Char('A') && cc <= QLatin1Char('Z'))
0113                          || (cc >= QLatin1Char('a') && cc <= QLatin1Char('z'))
0114                          || (cc >= QLatin1Char('0') && cc <= QLatin1Char('9'))); /* clang-format on */
0115                 if (cc == QLatin1Char('=')) {
0116                     goto metaerr;
0117                 }
0118             }
0119         }
0120     notilde:
0121         do {
0122             if (c == QLatin1Char('\'')) {
0123                 int spos = pos;
0124                 do {
0125                     if (pos >= args.length()) {
0126                         goto quoteerr;
0127                     }
0128                     c = args.unicode()[pos++];
0129                 } while (c != QLatin1Char('\''));
0130                 cret += QStringView(args).mid(spos, pos - spos - 1);
0131             } else if (c == QLatin1Char('"')) {
0132                 for (;;) {
0133                     if (pos >= args.length()) {
0134                         goto quoteerr;
0135                     }
0136                     c = args.unicode()[pos++];
0137                     if (c == QLatin1Char('"')) {
0138                         break;
0139                     }
0140                     if (c == QLatin1Char('\\')) {
0141                         if (pos >= args.length()) {
0142                             goto quoteerr;
0143                         }
0144                         c = args.unicode()[pos++];
0145                         if (c != QLatin1Char('"') //
0146                             && c != QLatin1Char('\\') //
0147                             && !((flags & AbortOnMeta) && (c == QLatin1Char('$') || c == QLatin1Char('`')))) {
0148                             cret += QLatin1Char('\\');
0149                         }
0150                     } else if ((flags & AbortOnMeta) && (c == QLatin1Char('$') || c == QLatin1Char('`'))) {
0151                         goto metaerr;
0152                     }
0153                     cret += c;
0154                 }
0155             } else if (c == QLatin1Char('$') && pos < args.length() && args.unicode()[pos] == QLatin1Char('\'')) {
0156                 pos++;
0157                 for (;;) {
0158                     if (pos >= args.length()) {
0159                         goto quoteerr;
0160                     }
0161                     c = args.unicode()[pos++];
0162                     if (c == QLatin1Char('\'')) {
0163                         break;
0164                     }
0165                     if (c == QLatin1Char('\\')) {
0166                         if (pos >= args.length()) {
0167                             goto quoteerr;
0168                         }
0169                         c = args.unicode()[pos++];
0170                         switch (c.toLatin1()) {
0171                         case 'a':
0172                             cret += QLatin1Char('\a');
0173                             break;
0174                         case 'b':
0175                             cret += QLatin1Char('\b');
0176                             break;
0177                         case 'e':
0178                             cret += QLatin1Char('\033');
0179                             break;
0180                         case 'f':
0181                             cret += QLatin1Char('\f');
0182                             break;
0183                         case 'n':
0184                             cret += QLatin1Char('\n');
0185                             break;
0186                         case 'r':
0187                             cret += QLatin1Char('\r');
0188                             break;
0189                         case 't':
0190                             cret += QLatin1Char('\t');
0191                             break;
0192                         case '\\':
0193                             cret += QLatin1Char('\\');
0194                             break;
0195                         case '\'':
0196                             cret += QLatin1Char('\'');
0197                             break;
0198                         case 'c':
0199                             if (pos >= args.length()) {
0200                                 goto quoteerr;
0201                             }
0202                             cret += QChar::fromLatin1(args.unicode()[pos++].toLatin1() & 31);
0203                             break;
0204                         case 'x': {
0205                             if (pos >= args.length()) {
0206                                 goto quoteerr;
0207                             }
0208                             int hv = fromHex(args.unicode()[pos++]);
0209                             if (hv < 0) {
0210                                 goto quoteerr;
0211                             }
0212                             if (pos < args.length()) {
0213                                 int hhv = fromHex(args.unicode()[pos]);
0214                                 if (hhv > 0) {
0215                                     hv = hv * 16 + hhv;
0216                                     pos++;
0217                                 }
0218                                 cret += QChar(hv);
0219                             }
0220                             break;
0221                         }
0222                         default:
0223                             if (c.toLatin1() >= '0' && c.toLatin1() <= '7') {
0224                                 char cAscii = c.toLatin1();
0225                                 int hv = cAscii - '0';
0226                                 for (int i = 0; i < 2; i++) {
0227                                     if (pos >= args.length()) {
0228                                         break;
0229                                     }
0230                                     c = args.unicode()[pos];
0231                                     if (c.toLatin1() < '0' || c.toLatin1() > '7') {
0232                                         break;
0233                                     }
0234                                     hv = hv * 8 + (c.toLatin1() - '0');
0235                                     pos++;
0236                                 }
0237                                 cret += QChar(hv);
0238                             } else {
0239                                 cret += QLatin1Char('\\');
0240                                 cret += c;
0241                             }
0242                             break;
0243                         }
0244                     } else {
0245                         cret += c;
0246                     }
0247                 }
0248             } else {
0249                 if (c == QLatin1Char('\\')) {
0250                     if (pos >= args.length()) {
0251                         goto quoteerr;
0252                     }
0253                     c = args.unicode()[pos++];
0254                 } else if ((flags & AbortOnMeta) && isMeta(c)) {
0255                     goto metaerr;
0256                 }
0257                 cret += c;
0258             }
0259             if (pos >= args.length()) {
0260                 break;
0261             }
0262             c = args.unicode()[pos++];
0263         } while (c != QLatin1Char(' '));
0264         ret += cret;
0265         firstword = false;
0266     }
0267 
0268 okret:
0269     if (err) {
0270         *err = NoError;
0271     }
0272     return ret;
0273 
0274 quoteerr:
0275     if (err) {
0276         *err = BadQuoting;
0277     }
0278     return QStringList();
0279 
0280 metaerr:
0281     if (err) {
0282         *err = FoundMeta;
0283     }
0284     return QStringList();
0285 }
0286 
0287 inline static bool isSpecial(QChar cUnicode)
0288 {
0289     static const uchar iqm[] = {0xff, 0xff, 0xff, 0xff, 0xdf, 0x07, 0x00, 0xd8, 0x00, 0x00, 0x00, 0x38, 0x01, 0x00, 0x00, 0x78}; // 0-32 \'"$`<>|;&(){}*?#!~[]
0290 
0291     uint c = cUnicode.unicode();
0292     return ((c < sizeof(iqm) * 8) && (iqm[c / 8] & (1 << (c & 7))));
0293 }
0294 
0295 QString KShell::quoteArg(const QString &arg)
0296 {
0297     if (!arg.length()) {
0298         return QStringLiteral("''");
0299     }
0300     for (int i = 0; i < arg.length(); i++) {
0301         if (isSpecial(arg.unicode()[i])) {
0302             QChar q(QLatin1Char('\''));
0303             return q + QString(arg).replace(q, QLatin1String("'\\''")) + q;
0304         }
0305     }
0306     return arg;
0307 }