1 | /* |
2 | This file is part of the KDE libraries |
3 | |
4 | SPDX-FileCopyrightText: 2002-2003 Oswald Buddenhagen <ossi@kde.org> |
5 | SPDX-FileCopyrightText: 2003 Waldo Bastian <bastian@kde.org> |
6 | |
7 | SPDX-License-Identifier: LGPL-2.0-or-later |
8 | */ |
9 | |
10 | #include "kmacroexpander_p.h" |
11 | |
12 | #include <QHash> |
13 | |
14 | KMacroExpanderBase::KMacroExpanderBase(QChar c) |
15 | : d(new KMacroExpanderBasePrivate(c)) |
16 | { |
17 | } |
18 | |
19 | KMacroExpanderBase::~KMacroExpanderBase() = default; |
20 | |
21 | void KMacroExpanderBase::setEscapeChar(QChar c) |
22 | { |
23 | d->escapechar = c; |
24 | } |
25 | |
26 | QChar KMacroExpanderBase::escapeChar() const |
27 | { |
28 | return d->escapechar; |
29 | } |
30 | |
31 | void KMacroExpanderBase::expandMacros(QString &str) |
32 | { |
33 | int pos; |
34 | int len; |
35 | ushort ec = d->escapechar.unicode(); |
36 | QStringList rst; |
37 | QString rsts; |
38 | |
39 | for (pos = 0; pos < str.length();) { |
40 | if (ec != 0) { |
41 | if (str.unicode()[pos].unicode() != ec) { |
42 | goto nohit; |
43 | } |
44 | if (!(len = expandEscapedMacro(str, pos, ret&: rst))) { |
45 | goto nohit; |
46 | } |
47 | } else { |
48 | if (!(len = expandPlainMacro(str, pos, ret&: rst))) { |
49 | goto nohit; |
50 | } |
51 | } |
52 | if (len < 0) { |
53 | pos -= len; |
54 | continue; |
55 | } |
56 | rsts = rst.join(sep: QLatin1Char(' ')); |
57 | rst.clear(); |
58 | str.replace(i: pos, len, after: rsts); |
59 | pos += rsts.length(); |
60 | continue; |
61 | nohit: |
62 | pos++; |
63 | } |
64 | } |
65 | |
66 | bool KMacroExpanderBase::expandMacrosShellQuote(QString &str) |
67 | { |
68 | int pos = 0; |
69 | return expandMacrosShellQuote(str, pos) && pos == str.length(); |
70 | } |
71 | |
72 | int KMacroExpanderBase::expandPlainMacro(const QString &, int, QStringList &) |
73 | { |
74 | qFatal(msg: "KMacroExpanderBase::expandPlainMacro called!" ); |
75 | return 0; |
76 | } |
77 | |
78 | int KMacroExpanderBase::expandEscapedMacro(const QString &, int, QStringList &) |
79 | { |
80 | qFatal(msg: "KMacroExpanderBase::expandEscapedMacro called!" ); |
81 | return 0; |
82 | } |
83 | |
84 | ////////////////////////////////////////////////// |
85 | |
86 | template<typename KT, typename VT> |
87 | class KMacroMapExpander : public KMacroExpanderBase |
88 | { |
89 | public: |
90 | KMacroMapExpander(const QHash<KT, VT> &map, QChar c = QLatin1Char('%')) |
91 | : KMacroExpanderBase(c) |
92 | , macromap(map) |
93 | { |
94 | } |
95 | |
96 | protected: |
97 | int expandPlainMacro(const QString &str, int pos, QStringList &ret) override; |
98 | int expandEscapedMacro(const QString &str, int pos, QStringList &ret) override; |
99 | |
100 | private: |
101 | QHash<KT, VT> macromap; |
102 | }; |
103 | |
104 | //////// |
105 | |
106 | static bool isIdentifier(ushort c) |
107 | { |
108 | return c == '_' || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9'); |
109 | } |
110 | |
111 | //////// |
112 | |
113 | template<typename VT> |
114 | class KMacroMapExpander<QChar, VT> : public KMacroExpanderBase |
115 | { |
116 | public: |
117 | KMacroMapExpander(const QHash<QChar, VT> &map, QChar c = QLatin1Char('%')) |
118 | : KMacroExpanderBase(c) |
119 | , macromap(map) |
120 | { |
121 | } |
122 | |
123 | protected: |
124 | int expandPlainMacro(const QString &str, int pos, QStringList &ret) override; |
125 | int expandEscapedMacro(const QString &str, int pos, QStringList &ret) override; |
126 | |
127 | private: |
128 | QHash<QChar, VT> macromap; |
129 | }; |
130 | |
131 | template<typename VT> |
132 | int KMacroMapExpander<QChar, VT>::expandPlainMacro(const QString &str, int pos, QStringList &ret) |
133 | { |
134 | typename QHash<QChar, VT>::const_iterator it = macromap.constFind(str.unicode()[pos]); |
135 | if (it != macromap.constEnd()) { |
136 | ret += it.value(); |
137 | return 1; |
138 | } |
139 | return 0; |
140 | } |
141 | |
142 | template<typename VT> |
143 | int KMacroMapExpander<QChar, VT>::expandEscapedMacro(const QString &str, int pos, QStringList &ret) |
144 | { |
145 | if (str.length() <= pos + 1) { |
146 | return 0; |
147 | } |
148 | |
149 | if (str.unicode()[pos + 1] == escapeChar()) { |
150 | ret += QString(escapeChar()); |
151 | return 2; |
152 | } |
153 | typename QHash<QChar, VT>::const_iterator it = macromap.constFind(str.unicode()[pos + 1]); |
154 | if (it != macromap.constEnd()) { |
155 | ret += it.value(); |
156 | return 2; |
157 | } |
158 | |
159 | return 0; |
160 | } |
161 | |
162 | template<typename VT> |
163 | class KMacroMapExpander<QString, VT> : public KMacroExpanderBase |
164 | { |
165 | public: |
166 | KMacroMapExpander(const QHash<QString, VT> &map, QChar c = QLatin1Char('%')) |
167 | : KMacroExpanderBase(c) |
168 | , macromap(map) |
169 | { |
170 | } |
171 | |
172 | protected: |
173 | int expandPlainMacro(const QString &str, int pos, QStringList &ret) override; |
174 | int expandEscapedMacro(const QString &str, int pos, QStringList &ret) override; |
175 | |
176 | private: |
177 | QHash<QString, VT> macromap; |
178 | }; |
179 | |
180 | template<typename VT> |
181 | int KMacroMapExpander<QString, VT>::expandPlainMacro(const QString &str, int pos, QStringList &ret) |
182 | { |
183 | if (pos && isIdentifier(c: str.unicode()[pos - 1].unicode())) { |
184 | return 0; |
185 | } |
186 | int sl; |
187 | for (sl = 0; isIdentifier(c: str.unicode()[pos + sl].unicode()); sl++) { |
188 | ; |
189 | } |
190 | if (!sl) { |
191 | return 0; |
192 | } |
193 | typename QHash<QString, VT>::const_iterator it = macromap.constFind(str.mid(position: pos, n: sl)); |
194 | if (it != macromap.constEnd()) { |
195 | ret += it.value(); |
196 | return sl; |
197 | } |
198 | return 0; |
199 | } |
200 | |
201 | template<typename VT> |
202 | int KMacroMapExpander<QString, VT>::expandEscapedMacro(const QString &str, int pos, QStringList &ret) |
203 | { |
204 | if (str.length() <= pos + 1) { |
205 | return 0; |
206 | } |
207 | |
208 | if (str.unicode()[pos + 1] == escapeChar()) { |
209 | ret += QString(escapeChar()); |
210 | return 2; |
211 | } |
212 | int sl; |
213 | int rsl; |
214 | int rpos; |
215 | if (str.unicode()[pos + 1].unicode() == '{') { |
216 | rpos = pos + 2; |
217 | if ((sl = str.indexOf(c: QLatin1Char('}'), from: rpos)) < 0) { |
218 | return 0; |
219 | } |
220 | sl -= rpos; |
221 | rsl = sl + 3; |
222 | } else { |
223 | rpos = pos + 1; |
224 | for (sl = 0; isIdentifier(c: str.unicode()[rpos + sl].unicode()); ++sl) { |
225 | ; |
226 | } |
227 | rsl = sl + 1; |
228 | } |
229 | if (!sl) { |
230 | return 0; |
231 | } |
232 | typename QHash<QString, VT>::const_iterator it = macromap.constFind(str.mid(position: rpos, n: sl)); |
233 | if (it != macromap.constEnd()) { |
234 | ret += it.value(); |
235 | return rsl; |
236 | } |
237 | return 0; |
238 | } |
239 | |
240 | //////////// |
241 | |
242 | int KCharMacroExpander::expandPlainMacro(const QString &str, int pos, QStringList &ret) |
243 | { |
244 | if (expandMacro(chr: str.unicode()[pos], ret)) { |
245 | return 1; |
246 | } |
247 | return 0; |
248 | } |
249 | |
250 | int KCharMacroExpander::expandEscapedMacro(const QString &str, int pos, QStringList &ret) |
251 | { |
252 | if (str.length() <= pos + 1) { |
253 | return 0; |
254 | } |
255 | |
256 | if (str.unicode()[pos + 1] == escapeChar()) { |
257 | ret += QString(escapeChar()); |
258 | return 2; |
259 | } |
260 | if (expandMacro(chr: str.unicode()[pos + 1], ret)) { |
261 | return 2; |
262 | } |
263 | return 0; |
264 | } |
265 | |
266 | int KWordMacroExpander::expandPlainMacro(const QString &str, int pos, QStringList &ret) |
267 | { |
268 | if (pos && isIdentifier(c: str.unicode()[pos - 1].unicode())) { |
269 | return 0; |
270 | } |
271 | int sl; |
272 | for (sl = 0; isIdentifier(c: str.unicode()[pos + sl].unicode()); sl++) { |
273 | ; |
274 | } |
275 | if (!sl) { |
276 | return 0; |
277 | } |
278 | if (expandMacro(str: str.mid(position: pos, n: sl), ret)) { |
279 | return sl; |
280 | } |
281 | return 0; |
282 | } |
283 | |
284 | int KWordMacroExpander::expandEscapedMacro(const QString &str, int pos, QStringList &ret) |
285 | { |
286 | if (str.length() <= pos + 1) { |
287 | return 0; |
288 | } |
289 | |
290 | if (str.unicode()[pos + 1] == escapeChar()) { |
291 | ret += QString(escapeChar()); |
292 | return 2; |
293 | } |
294 | int sl; |
295 | int rsl; |
296 | int rpos; |
297 | if (str.unicode()[pos + 1].unicode() == '{') { |
298 | rpos = pos + 2; |
299 | if ((sl = str.indexOf(c: QLatin1Char('}'), from: rpos)) < 0) { |
300 | return 0; |
301 | } |
302 | sl -= rpos; |
303 | rsl = sl + 3; |
304 | } else { |
305 | rpos = pos + 1; |
306 | for (sl = 0; isIdentifier(c: str.unicode()[rpos + sl].unicode()); ++sl) { |
307 | ; |
308 | } |
309 | rsl = sl + 1; |
310 | } |
311 | if (!sl) { |
312 | return 0; |
313 | } |
314 | if (expandMacro(str: str.mid(position: rpos, n: sl), ret)) { |
315 | return rsl; |
316 | } |
317 | return 0; |
318 | } |
319 | |
320 | //////////// |
321 | |
322 | template<typename KT, typename VT> |
323 | inline QString TexpandMacros(const QString &ostr, const QHash<KT, VT> &map, QChar c) |
324 | { |
325 | QString str(ostr); |
326 | KMacroMapExpander<KT, VT> kmx(map, c); |
327 | kmx.expandMacros(str); |
328 | return str; |
329 | } |
330 | |
331 | template<typename KT, typename VT> |
332 | inline QString TexpandMacrosShellQuote(const QString &ostr, const QHash<KT, VT> &map, QChar c) |
333 | { |
334 | QString str(ostr); |
335 | KMacroMapExpander<KT, VT> kmx(map, c); |
336 | if (!kmx.expandMacrosShellQuote(str)) { |
337 | return QString(); |
338 | } |
339 | return str; |
340 | } |
341 | |
342 | // public API |
343 | namespace KMacroExpander |
344 | { |
345 | QString expandMacros(const QString &ostr, const QHash<QChar, QString> &map, QChar c) |
346 | { |
347 | return TexpandMacros(ostr, map, c); |
348 | } |
349 | QString expandMacrosShellQuote(const QString &ostr, const QHash<QChar, QString> &map, QChar c) |
350 | { |
351 | return TexpandMacrosShellQuote(ostr, map, c); |
352 | } |
353 | QString expandMacros(const QString &ostr, const QHash<QString, QString> &map, QChar c) |
354 | { |
355 | return TexpandMacros(ostr, map, c); |
356 | } |
357 | QString expandMacrosShellQuote(const QString &ostr, const QHash<QString, QString> &map, QChar c) |
358 | { |
359 | return TexpandMacrosShellQuote(ostr, map, c); |
360 | } |
361 | QString expandMacros(const QString &ostr, const QHash<QChar, QStringList> &map, QChar c) |
362 | { |
363 | return TexpandMacros(ostr, map, c); |
364 | } |
365 | QString expandMacrosShellQuote(const QString &ostr, const QHash<QChar, QStringList> &map, QChar c) |
366 | { |
367 | return TexpandMacrosShellQuote(ostr, map, c); |
368 | } |
369 | QString expandMacros(const QString &ostr, const QHash<QString, QStringList> &map, QChar c) |
370 | { |
371 | return TexpandMacros(ostr, map, c); |
372 | } |
373 | QString expandMacrosShellQuote(const QString &ostr, const QHash<QString, QStringList> &map, QChar c) |
374 | { |
375 | return TexpandMacrosShellQuote(ostr, map, c); |
376 | } |
377 | |
378 | } // namespace |
379 | |