KCoreAddons

kshell_unix.cpp
1 /*
2  This file is part of the KDE libraries
3 
4  SPDX-FileCopyrightText: 2003, 2007 Oswald Buddenhagen <[email protected]>
5 
6  SPDX-License-Identifier: LGPL-2.0-or-later
7 */
8 
9 #include "kshell.h"
10 #include "kshell_p.h"
11 
12 #include <kuser.h>
13 
14 #include <QChar>
15 #include <QStringList>
16 
17 static int fromHex(QChar cUnicode)
18 {
19  char c = cUnicode.toLatin1();
20 
21  if (c >= '0' && c <= '9') {
22  return c - '0';
23  } else if (c >= 'A' && c <= 'F') {
24  return c - 'A' + 10;
25  } else if (c >= 'a' && c <= 'f') {
26  return c - 'a' + 10;
27  }
28  return -1;
29 }
30 
31 inline static bool isQuoteMeta(QChar cUnicode)
32 {
33  char c = cUnicode.toLatin1();
34  return c == '\\' || c == '\'' || c == '"' || c == '$';
35 }
36 
37 inline static bool isMeta(QChar cUnicode)
38 {
39  static const uchar iqm[] = {0x00, 0x00, 0x00, 0x00, 0xdc, 0x07, 0x00, 0xd8, 0x00, 0x00, 0x00, 0x38, 0x01, 0x00, 0x00, 0x38}; // \'"$`<>|;&(){}*?#[]
40 
41  uint c = cUnicode.unicode();
42 
43  return (c < sizeof(iqm) * 8) && (iqm[c / 8] & (1 << (c & 7)));
44 }
45 
47 {
48  QStringList ret;
49  bool firstword = flags & AbortOnMeta;
50 
51  for (int pos = 0;;) {
52  QChar c;
53  do {
54  if (pos >= args.length()) {
55  goto okret;
56  }
57  c = args.unicode()[pos++];
58  } while (c == QLatin1Char(' '));
59  QString cret;
60  if ((flags & TildeExpand) && c == QLatin1Char('~')) {
61  int opos = pos;
62  for (;; pos++) {
63  if (pos >= args.length()) {
64  break;
65  }
66  c = args.unicode()[pos];
67  if (c == QLatin1Char('/') || c == QLatin1Char(' ')) {
68  break;
69  }
70  if (isQuoteMeta(c)) {
71  pos = opos;
72  c = QLatin1Char('~');
73  goto notilde;
74  }
75  if ((flags & AbortOnMeta) && isMeta(c)) {
76  goto metaerr;
77  }
78  }
79  QString ccret = homeDir(args.mid(opos, pos - opos));
80  if (ccret.isEmpty()) {
81  pos = opos;
82  c = QLatin1Char('~');
83  goto notilde;
84  }
85  if (pos >= args.length()) {
86  ret += ccret;
87  goto okret;
88  }
89  pos++;
90  if (c == QLatin1Char(' ')) {
91  ret += ccret;
92  firstword = false;
93  continue;
94  }
95  cret = ccret;
96  }
97  // before the notilde label, as a tilde does not match anyway
98  if (firstword) {
99  if (c == QLatin1Char('_') //
100  || (c >= QLatin1Char('A') && c <= QLatin1Char('Z')) //
101  || (c >= QLatin1Char('a') && c <= QLatin1Char('z'))) {
102  int pos2 = pos;
103  QChar cc;
104  do {
105  if (pos2 >= args.length()) {
106  // Exactly one word
107  ret += args.mid(pos - 1);
108  goto okret;
109  }
110  cc = args.unicode()[pos2++];
111  } while (cc == QLatin1Char('_') /* clang-format off */
112  || (cc >= QLatin1Char('A') && cc <= QLatin1Char('Z'))
113  || (cc >= QLatin1Char('a') && cc <= QLatin1Char('z'))
114  || (cc >= QLatin1Char('0') && cc <= QLatin1Char('9'))); /* clang-format on */
115  if (cc == QLatin1Char('=')) {
116  goto metaerr;
117  }
118  }
119  }
120  notilde:
121  do {
122  if (c == QLatin1Char('\'')) {
123  int spos = pos;
124  do {
125  if (pos >= args.length()) {
126  goto quoteerr;
127  }
128  c = args.unicode()[pos++];
129  } while (c != QLatin1Char('\''));
130  cret += args.midRef(spos, pos - spos - 1);
131  } else if (c == QLatin1Char('"')) {
132  for (;;) {
133  if (pos >= args.length()) {
134  goto quoteerr;
135  }
136  c = args.unicode()[pos++];
137  if (c == QLatin1Char('"')) {
138  break;
139  }
140  if (c == QLatin1Char('\\')) {
141  if (pos >= args.length()) {
142  goto quoteerr;
143  }
144  c = args.unicode()[pos++];
145  if (c != QLatin1Char('"') //
146  && c != QLatin1Char('\\') //
147  && !((flags & AbortOnMeta) && (c == QLatin1Char('$') || c == QLatin1Char('`')))) {
148  cret += QLatin1Char('\\');
149  }
150  } else if ((flags & AbortOnMeta) && (c == QLatin1Char('$') || c == QLatin1Char('`'))) {
151  goto metaerr;
152  }
153  cret += c;
154  }
155  } else if (c == QLatin1Char('$') && pos < args.length() && args.unicode()[pos] == QLatin1Char('\'')) {
156  pos++;
157  for (;;) {
158  if (pos >= args.length()) {
159  goto quoteerr;
160  }
161  c = args.unicode()[pos++];
162  if (c == QLatin1Char('\'')) {
163  break;
164  }
165  if (c == QLatin1Char('\\')) {
166  if (pos >= args.length()) {
167  goto quoteerr;
168  }
169  c = args.unicode()[pos++];
170  switch (c.toLatin1()) {
171  case 'a':
172  cret += QLatin1Char('\a');
173  break;
174  case 'b':
175  cret += QLatin1Char('\b');
176  break;
177  case 'e':
178  cret += QLatin1Char('\033');
179  break;
180  case 'f':
181  cret += QLatin1Char('\f');
182  break;
183  case 'n':
184  cret += QLatin1Char('\n');
185  break;
186  case 'r':
187  cret += QLatin1Char('\r');
188  break;
189  case 't':
190  cret += QLatin1Char('\t');
191  break;
192  case '\\':
193  cret += QLatin1Char('\\');
194  break;
195  case '\'':
196  cret += QLatin1Char('\'');
197  break;
198  case 'c':
199  if (pos >= args.length()) {
200  goto quoteerr;
201  }
202  cret += QChar::fromLatin1(args.unicode()[pos++].toLatin1() & 31);
203  break;
204  case 'x': {
205  if (pos >= args.length()) {
206  goto quoteerr;
207  }
208  int hv = fromHex(args.unicode()[pos++]);
209  if (hv < 0) {
210  goto quoteerr;
211  }
212  if (pos < args.length()) {
213  int hhv = fromHex(args.unicode()[pos]);
214  if (hhv > 0) {
215  hv = hv * 16 + hhv;
216  pos++;
217  }
218  cret += QChar(hv);
219  }
220  break;
221  }
222  default:
223  if (c.toLatin1() >= '0' && c.toLatin1() <= '7') {
224  char cAscii = c.toLatin1();
225  int hv = cAscii - '0';
226  for (int i = 0; i < 2; i++) {
227  if (pos >= args.length()) {
228  break;
229  }
230  c = args.unicode()[pos];
231  if (c.toLatin1() < '0' || c.toLatin1() > '7') {
232  break;
233  }
234  hv = hv * 8 + (c.toLatin1() - '0');
235  pos++;
236  }
237  cret += QChar(hv);
238  } else {
239  cret += QLatin1Char('\\');
240  cret += c;
241  }
242  break;
243  }
244  } else {
245  cret += c;
246  }
247  }
248  } else {
249  if (c == QLatin1Char('\\')) {
250  if (pos >= args.length()) {
251  goto quoteerr;
252  }
253  c = args.unicode()[pos++];
254  } else if ((flags & AbortOnMeta) && isMeta(c)) {
255  goto metaerr;
256  }
257  cret += c;
258  }
259  if (pos >= args.length()) {
260  break;
261  }
262  c = args.unicode()[pos++];
263  } while (c != QLatin1Char(' '));
264  ret += cret;
265  firstword = false;
266  }
267 
268 okret:
269  if (err) {
270  *err = NoError;
271  }
272  return ret;
273 
274 quoteerr:
275  if (err) {
276  *err = BadQuoting;
277  }
278  return QStringList();
279 
280 metaerr:
281  if (err) {
282  *err = FoundMeta;
283  }
284  return QStringList();
285 }
286 
287 inline static bool isSpecial(QChar cUnicode)
288 {
289  static const uchar iqm[] = {0xff, 0xff, 0xff, 0xff, 0xdf, 0x07, 0x00, 0xd8, 0x00, 0x00, 0x00, 0x38, 0x01, 0x00, 0x00, 0x78}; // 0-32 \'"$`<>|;&(){}*?#!~[]
290 
291  uint c = cUnicode.unicode();
292  return ((c < sizeof(iqm) * 8) && (iqm[c / 8] & (1 << (c & 7))));
293 }
294 
296 {
297  if (!arg.length()) {
298  return QStringLiteral("''");
299  }
300  for (int i = 0; i < arg.length(); i++)
301  if (isSpecial(arg.unicode()[i])) {
302  QChar q(QLatin1Char('\''));
303  return q + QString(arg).replace(q, QLatin1String("'\\''")) + q;
304  }
305  return arg;
306 }
The AbortOnMeta flag was set and an unhandled shell meta character was encountered.
Definition: kshell.h:93
QChar fromLatin1(char c)
bool isEmpty() const const
Errors
Status codes from splitArgs()
Definition: kshell.h:78
KCOREADDONS_EXPORT QStringList splitArgs(const QString &cmd, Options flags=NoOptions, Errors *err=nullptr)
Splits cmd according to system shell word splitting and quoting rules.
Definition: kshell_unix.cpp:46
ushort unicode() const const
KCOREADDONS_EXPORT QString quoteArg(const QString &arg)
Quotes arg according to system shell rules.
QStringRef midRef(int position, int n) const const
char toLatin1() const const
QString & replace(int position, int n, QChar after)
const QChar * unicode() const const
QString mid(int position, int n) const const
Indicates a parsing error, like an unterminated quoted string.
Definition: kshell.h:87
int length() const const
Put the parser into full shell mode and bail out if a too complex construct is encountered.
Definition: kshell.h:67
Perform tilde expansion.
Definition: kshell.h:36
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Sun Apr 18 2021 23:02:02 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.