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
14KMacroExpanderBase::KMacroExpanderBase(QChar c)
15 : d(new KMacroExpanderBasePrivate(c))
16{
17}
18
19KMacroExpanderBase::~KMacroExpanderBase() = default;
20
21void KMacroExpanderBase::setEscapeChar(QChar c)
22{
23 d->escapechar = c;
24}
25
26QChar KMacroExpanderBase::escapeChar() const
27{
28 return d->escapechar;
29}
30
31void 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
66bool KMacroExpanderBase::expandMacrosShellQuote(QString &str)
67{
68 int pos = 0;
69 return expandMacrosShellQuote(str, pos) && pos == str.length();
70}
71
72int KMacroExpanderBase::expandPlainMacro(const QString &, int, QStringList &)
73{
74 qFatal(msg: "KMacroExpanderBase::expandPlainMacro called!");
75 return 0;
76}
77
78int KMacroExpanderBase::expandEscapedMacro(const QString &, int, QStringList &)
79{
80 qFatal(msg: "KMacroExpanderBase::expandEscapedMacro called!");
81 return 0;
82}
83
84//////////////////////////////////////////////////
85
86template<typename KT, typename VT>
87class KMacroMapExpander : public KMacroExpanderBase
88{
89public:
90 KMacroMapExpander(const QHash<KT, VT> &map, QChar c = QLatin1Char('%'))
91 : KMacroExpanderBase(c)
92 , macromap(map)
93 {
94 }
95
96protected:
97 int expandPlainMacro(const QString &str, int pos, QStringList &ret) override;
98 int expandEscapedMacro(const QString &str, int pos, QStringList &ret) override;
99
100private:
101 QHash<KT, VT> macromap;
102};
103
104////////
105
106static bool isIdentifier(ushort c)
107{
108 return c == '_' || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9');
109}
110
111////////
112
113template<typename VT>
114class KMacroMapExpander<QChar, VT> : public KMacroExpanderBase
115{
116public:
117 KMacroMapExpander(const QHash<QChar, VT> &map, QChar c = QLatin1Char('%'))
118 : KMacroExpanderBase(c)
119 , macromap(map)
120 {
121 }
122
123protected:
124 int expandPlainMacro(const QString &str, int pos, QStringList &ret) override;
125 int expandEscapedMacro(const QString &str, int pos, QStringList &ret) override;
126
127private:
128 QHash<QChar, VT> macromap;
129};
130
131template<typename VT>
132int 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
142template<typename VT>
143int 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
162template<typename VT>
163class KMacroMapExpander<QString, VT> : public KMacroExpanderBase
164{
165public:
166 KMacroMapExpander(const QHash<QString, VT> &map, QChar c = QLatin1Char('%'))
167 : KMacroExpanderBase(c)
168 , macromap(map)
169 {
170 }
171
172protected:
173 int expandPlainMacro(const QString &str, int pos, QStringList &ret) override;
174 int expandEscapedMacro(const QString &str, int pos, QStringList &ret) override;
175
176private:
177 QHash<QString, VT> macromap;
178};
179
180template<typename VT>
181int 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
201template<typename VT>
202int 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
242int 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
250int 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
266int 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
284int 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
322template<typename KT, typename VT>
323inline 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
331template<typename KT, typename VT>
332inline 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
343namespace KMacroExpander
344{
345QString expandMacros(const QString &ostr, const QHash<QChar, QString> &map, QChar c)
346{
347 return TexpandMacros(ostr, map, c);
348}
349QString expandMacrosShellQuote(const QString &ostr, const QHash<QChar, QString> &map, QChar c)
350{
351 return TexpandMacrosShellQuote(ostr, map, c);
352}
353QString expandMacros(const QString &ostr, const QHash<QString, QString> &map, QChar c)
354{
355 return TexpandMacros(ostr, map, c);
356}
357QString expandMacrosShellQuote(const QString &ostr, const QHash<QString, QString> &map, QChar c)
358{
359 return TexpandMacrosShellQuote(ostr, map, c);
360}
361QString expandMacros(const QString &ostr, const QHash<QChar, QStringList> &map, QChar c)
362{
363 return TexpandMacros(ostr, map, c);
364}
365QString expandMacrosShellQuote(const QString &ostr, const QHash<QChar, QStringList> &map, QChar c)
366{
367 return TexpandMacrosShellQuote(ostr, map, c);
368}
369QString expandMacros(const QString &ostr, const QHash<QString, QStringList> &map, QChar c)
370{
371 return TexpandMacros(ostr, map, c);
372}
373QString expandMacrosShellQuote(const QString &ostr, const QHash<QString, QStringList> &map, QChar c)
374{
375 return TexpandMacrosShellQuote(ostr, map, c);
376}
377
378} // namespace
379

source code of kcoreaddons/src/lib/text/kmacroexpander.cpp