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 }