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

KDE's Doxygen guidelines are available online.