KCoreAddons

kmacroexpander_unix.cpp
1 /*
2  This file is part of the KDE libraries
3 
4  SPDX-FileCopyrightText: 2002-2003 Oswald Buddenhagen <[email protected]>
5  SPDX-FileCopyrightText: 2003 Waldo Bastian <[email protected]>
6 
7  SPDX-License-Identifier: LGPL-2.0-or-later
8 */
9 
10 #include "kmacroexpander_p.h"
11 
12 #include <QStringList>
13 #include <QStack>
14 #include <QRegularExpression>
15 
16 namespace KMacroExpander
17 {
18 
19 enum Quoting { noquote, singlequote, doublequote, dollarquote,
20  paren, subst, group, math
21  };
22 typedef struct {
23  Quoting current;
24  bool dquote;
25 } State;
26 typedef struct {
27  QString str;
28  int pos;
29 } Save;
30 
31 }
32 
33 using namespace KMacroExpander;
34 
35 #pragma message("TODO: Import these methods into Qt")
36 
37 inline static bool isSpecial(QChar cUnicode)
38 {
39  static const uchar iqm[] = {
40  0xff, 0xff, 0xff, 0xff, 0xdf, 0x07, 0x00, 0xd8,
41  0x00, 0x00, 0x00, 0x38, 0x01, 0x00, 0x00, 0x78
42  }; // 0-32 \'"$`<>|;&(){}*?#!~[]
43 
44  uint c = cUnicode.unicode();
45  return (c < sizeof(iqm) * 8) && (iqm[c / 8] & (1 << (c & 7)));
46 }
47 
48 static QString quoteArg(const QString &arg)
49 {
50  if (!arg.length()) {
51  return QStringLiteral("''");
52  }
53  for (int i = 0; i < arg.length(); i++)
54  if (isSpecial(arg.unicode()[i])) {
55  QChar q(QLatin1Char('\''));
56  return q + QString(arg).replace(q, QLatin1String("'\\''")) + q;
57  }
58  return arg;
59 }
60 
61 static QString joinArgs(const QStringList &args)
62 {
63  QString ret;
64  for (QStringList::ConstIterator it = args.begin(); it != args.end(); ++it) {
65  if (!ret.isEmpty()) {
66  ret.append(QLatin1Char(' '));
67  }
68  ret.append(quoteArg(*it));
69  }
70  return ret;
71 }
72 
74 {
75  int len;
76  int pos2;
77  ushort ec = d->escapechar.unicode();
78  State state = { noquote, false };
79  QStack<State> sstack;
80  QStack<Save> ostack;
81  QStringList rst;
82  QString rsts;
83 
84  while (pos < str.length()) {
85  ushort cc = str.unicode()[pos].unicode();
86  if (ec != 0) {
87  if (cc != ec) {
88  goto nohit;
89  }
90  if (!(len = expandEscapedMacro(str, pos, rst))) {
91  goto nohit;
92  }
93  } else {
94  if (!(len = expandPlainMacro(str, pos, rst))) {
95  goto nohit;
96  }
97  }
98  if (len < 0) {
99  pos -= len;
100  continue;
101  }
102  if (state.dquote) {
103  rsts = rst.join(QLatin1Char(' '));
104  rsts.replace(QRegularExpression(QStringLiteral("([$`\"\\\\])")), QStringLiteral("\\\\1"));
105  } else if (state.current == dollarquote) {
106  rsts = rst.join(QLatin1Char(' '));
107  rsts.replace(QRegularExpression(QStringLiteral("(['\\\\])")), QStringLiteral("\\\\1"));
108  } else if (state.current == singlequote) {
109  rsts = rst.join(QLatin1Char(' '));
110  rsts.replace(QLatin1Char('\''), QLatin1String("'\\''"));
111  } else {
112  if (rst.isEmpty()) {
113  str.remove(pos, len);
114  continue;
115  } else {
116  rsts = joinArgs(rst);
117  }
118  }
119  rst.clear();
120  str.replace(pos, len, rsts);
121  pos += rsts.length();
122  continue;
123  nohit:
124  if (state.current == singlequote) {
125  if (cc == '\'') {
126  state = sstack.pop();
127  }
128  } else if (cc == '\\') {
129  // always swallow the char -> prevent anomalies due to expansion
130  pos += 2;
131  continue;
132  } else if (state.current == dollarquote) {
133  if (cc == '\'') {
134  state = sstack.pop();
135  }
136  } else if (cc == '$') {
137  cc = str.unicode()[++pos].unicode();
138  if (cc == '(') {
139  sstack.push(state);
140  if (str.unicode()[pos + 1].unicode() == '(') {
141  Save sav = { str, pos + 2 };
142  ostack.push(sav);
143  state.current = math;
144  pos += 2;
145  continue;
146  } else {
147  state.current = paren;
148  state.dquote = false;
149  }
150  } else if (cc == '{') {
151  sstack.push(state);
152  state.current = subst;
153  } else if (!state.dquote) {
154  if (cc == '\'') {
155  sstack.push(state);
156  state.current = dollarquote;
157  } else if (cc == '"') {
158  sstack.push(state);
159  state.current = doublequote;
160  state.dquote = true;
161  }
162  }
163  // always swallow the char -> prevent anomalies due to expansion
164  } else if (cc == '`') {
165  str.replace(pos, 1, QStringLiteral("$( ")); // add space -> avoid creating $((
166  pos2 = pos += 3;
167  for (;;) {
168  if (pos2 >= str.length()) {
169  pos = pos2;
170  return false;
171  }
172  cc = str.unicode()[pos2].unicode();
173  if (cc == '`') {
174  break;
175  }
176  if (cc == '\\') {
177  cc = str.unicode()[++pos2].unicode();
178  if (cc == '$' || cc == '`' || cc == '\\' ||
179  (cc == '"' && state.dquote)) {
180  str.remove(pos2 - 1, 1);
181  continue;
182  }
183  }
184  pos2++;
185  }
186  str[pos2] = QLatin1Char(')');
187  sstack.push(state);
188  state.current = paren;
189  state.dquote = false;
190  continue;
191  } else if (state.current == doublequote) {
192  if (cc == '"') {
193  state = sstack.pop();
194  }
195  } else if (cc == '\'') {
196  if (!state.dquote) {
197  sstack.push(state);
198  state.current = singlequote;
199  }
200  } else if (cc == '"') {
201  if (!state.dquote) {
202  sstack.push(state);
203  state.current = doublequote;
204  state.dquote = true;
205  }
206  } else if (state.current == subst) {
207  if (cc == '}') {
208  state = sstack.pop();
209  }
210  } else if (cc == ')') {
211  if (state.current == math) {
212  if (str.unicode()[pos + 1].unicode() == ')') {
213  state = sstack.pop();
214  pos += 2;
215  } else {
216  // false hit: the $(( was a $( ( in fact
217  // ash does not care, but bash does
218  pos = ostack.top().pos;
219  str = ostack.top().str;
220  ostack.pop();
221  state.current = paren;
222  state.dquote = false;
223  sstack.push(state);
224  }
225  continue;
226  } else if (state.current == paren) {
227  state = sstack.pop();
228  } else {
229  break;
230  }
231  } else if (cc == '}') {
232  if (state.current == KMacroExpander::group) {
233  state = sstack.pop();
234  } else {
235  break;
236  }
237  } else if (cc == '(') {
238  sstack.push(state);
239  state.current = paren;
240  } else if (cc == '{') {
241  sstack.push(state);
242  state.current = KMacroExpander::group;
243  }
244  pos++;
245  }
246  return sstack.empty();
247 }
void clear()
QString & append(QChar ch)
bool expandMacrosShellQuote(QString &str, int &pos)
Perform safe macro expansion (substitution) on a string for use in shell commands.
void push(const T &t)
QString join(const QString &separator) const const
QString & remove(int position, int n)
bool isEmpty() const const
bool isEmpty() const const
ushort unicode() const const
QList::iterator end()
A group of functions providing macro expansion (substitution) in strings, optionally with quoting app...
QString & replace(int position, int n, QChar after)
const QChar * unicode() const const
typedef ConstIterator
int length() const const
bool empty() const const
State
QList::iterator begin()
T & top()
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Sat May 30 2020 23:10:51 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.