File indexing completed on 2024-05-12 15:34:58

0001 /*
0002     This file is part of the KDE libraries
0003 
0004     SPDX-FileCopyrightText: 2002-2003 Oswald Buddenhagen <ossi@kde.org>
0005     SPDX-FileCopyrightText: 2003 Waldo Bastian <bastian@kde.org>
0006 
0007     SPDX-License-Identifier: LGPL-2.0-or-later
0008 */
0009 
0010 #include "kmacroexpander_p.h"
0011 
0012 #include <QHash>
0013 
0014 KMacroExpanderBase::KMacroExpanderBase(QChar c)
0015     : d(new KMacroExpanderBasePrivate(c))
0016 {
0017 }
0018 
0019 KMacroExpanderBase::~KMacroExpanderBase() = default;
0020 
0021 void KMacroExpanderBase::setEscapeChar(QChar c)
0022 {
0023     d->escapechar = c;
0024 }
0025 
0026 QChar KMacroExpanderBase::escapeChar() const
0027 {
0028     return d->escapechar;
0029 }
0030 
0031 void KMacroExpanderBase::expandMacros(QString &str)
0032 {
0033     int pos;
0034     int len;
0035     ushort ec = d->escapechar.unicode();
0036     QStringList rst;
0037     QString rsts;
0038 
0039     for (pos = 0; pos < str.length();) {
0040         if (ec != 0) {
0041             if (str.unicode()[pos].unicode() != ec) {
0042                 goto nohit;
0043             }
0044             if (!(len = expandEscapedMacro(str, pos, rst))) {
0045                 goto nohit;
0046             }
0047         } else {
0048             if (!(len = expandPlainMacro(str, pos, rst))) {
0049                 goto nohit;
0050             }
0051         }
0052         if (len < 0) {
0053             pos -= len;
0054             continue;
0055         }
0056         rsts = rst.join(QLatin1Char(' '));
0057         rst.clear();
0058         str.replace(pos, len, rsts);
0059         pos += rsts.length();
0060         continue;
0061     nohit:
0062         pos++;
0063     }
0064 }
0065 
0066 bool KMacroExpanderBase::expandMacrosShellQuote(QString &str)
0067 {
0068     int pos = 0;
0069     return expandMacrosShellQuote(str, pos) && pos == str.length();
0070 }
0071 
0072 int KMacroExpanderBase::expandPlainMacro(const QString &, int, QStringList &)
0073 {
0074     qFatal("KMacroExpanderBase::expandPlainMacro called!");
0075     return 0;
0076 }
0077 
0078 int KMacroExpanderBase::expandEscapedMacro(const QString &, int, QStringList &)
0079 {
0080     qFatal("KMacroExpanderBase::expandEscapedMacro called!");
0081     return 0;
0082 }
0083 
0084 //////////////////////////////////////////////////
0085 
0086 template<typename KT, typename VT>
0087 class KMacroMapExpander : public KMacroExpanderBase
0088 {
0089 public:
0090     KMacroMapExpander(const QHash<KT, VT> &map, QChar c = QLatin1Char('%'))
0091         : KMacroExpanderBase(c)
0092         , macromap(map)
0093     {
0094     }
0095 
0096 protected:
0097     int expandPlainMacro(const QString &str, int pos, QStringList &ret) override;
0098     int expandEscapedMacro(const QString &str, int pos, QStringList &ret) override;
0099 
0100 private:
0101     QHash<KT, VT> macromap;
0102 };
0103 
0104 static QStringList &operator+=(QStringList &s, const QString &n)
0105 {
0106     s << n;
0107     return s;
0108 }
0109 
0110 ////////
0111 
0112 static bool isIdentifier(ushort c)
0113 {
0114     return c == '_' || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9');
0115 }
0116 
0117 ////////
0118 
0119 template<typename VT>
0120 class KMacroMapExpander<QChar, VT> : public KMacroExpanderBase
0121 {
0122 public:
0123     KMacroMapExpander(const QHash<QChar, VT> &map, QChar c = QLatin1Char('%'))
0124         : KMacroExpanderBase(c)
0125         , macromap(map)
0126     {
0127     }
0128 
0129 protected:
0130     int expandPlainMacro(const QString &str, int pos, QStringList &ret) override;
0131     int expandEscapedMacro(const QString &str, int pos, QStringList &ret) override;
0132 
0133 private:
0134     QHash<QChar, VT> macromap;
0135 };
0136 
0137 template<typename VT>
0138 int KMacroMapExpander<QChar, VT>::expandPlainMacro(const QString &str, int pos, QStringList &ret)
0139 {
0140     typename QHash<QChar, VT>::const_iterator it = macromap.constFind(str.unicode()[pos]);
0141     if (it != macromap.constEnd()) {
0142         ret += it.value();
0143         return 1;
0144     }
0145     return 0;
0146 }
0147 
0148 template<typename VT>
0149 int KMacroMapExpander<QChar, VT>::expandEscapedMacro(const QString &str, int pos, QStringList &ret)
0150 {
0151     if (str.length() <= pos + 1) {
0152         return 0;
0153     }
0154 
0155     if (str.unicode()[pos + 1] == escapeChar()) {
0156         ret += QString(escapeChar());
0157         return 2;
0158     }
0159     typename QHash<QChar, VT>::const_iterator it = macromap.constFind(str.unicode()[pos + 1]);
0160     if (it != macromap.constEnd()) {
0161         ret += it.value();
0162         return 2;
0163     }
0164 
0165     return 0;
0166 }
0167 
0168 template<typename VT>
0169 class KMacroMapExpander<QString, VT> : public KMacroExpanderBase
0170 {
0171 public:
0172     KMacroMapExpander(const QHash<QString, VT> &map, QChar c = QLatin1Char('%'))
0173         : KMacroExpanderBase(c)
0174         , macromap(map)
0175     {
0176     }
0177 
0178 protected:
0179     int expandPlainMacro(const QString &str, int pos, QStringList &ret) override;
0180     int expandEscapedMacro(const QString &str, int pos, QStringList &ret) override;
0181 
0182 private:
0183     QHash<QString, VT> macromap;
0184 };
0185 
0186 template<typename VT>
0187 int KMacroMapExpander<QString, VT>::expandPlainMacro(const QString &str, int pos, QStringList &ret)
0188 {
0189     if (pos && isIdentifier(str.unicode()[pos - 1].unicode())) {
0190         return 0;
0191     }
0192     int sl;
0193     for (sl = 0; isIdentifier(str.unicode()[pos + sl].unicode()); sl++) {
0194         ;
0195     }
0196     if (!sl) {
0197         return 0;
0198     }
0199     typename QHash<QString, VT>::const_iterator it = macromap.constFind(str.mid(pos, sl));
0200     if (it != macromap.constEnd()) {
0201         ret += it.value();
0202         return sl;
0203     }
0204     return 0;
0205 }
0206 
0207 template<typename VT>
0208 int KMacroMapExpander<QString, VT>::expandEscapedMacro(const QString &str, int pos, QStringList &ret)
0209 {
0210     if (str.length() <= pos + 1) {
0211         return 0;
0212     }
0213 
0214     if (str.unicode()[pos + 1] == escapeChar()) {
0215         ret += QString(escapeChar());
0216         return 2;
0217     }
0218     int sl;
0219     int rsl;
0220     int rpos;
0221     if (str.unicode()[pos + 1].unicode() == '{') {
0222         rpos = pos + 2;
0223         if ((sl = str.indexOf(QLatin1Char('}'), rpos)) < 0) {
0224             return 0;
0225         }
0226         sl -= rpos;
0227         rsl = sl + 3;
0228     } else {
0229         rpos = pos + 1;
0230         for (sl = 0; isIdentifier(str.unicode()[rpos + sl].unicode()); ++sl) {
0231             ;
0232         }
0233         rsl = sl + 1;
0234     }
0235     if (!sl) {
0236         return 0;
0237     }
0238     typename QHash<QString, VT>::const_iterator it = macromap.constFind(str.mid(rpos, sl));
0239     if (it != macromap.constEnd()) {
0240         ret += it.value();
0241         return rsl;
0242     }
0243     return 0;
0244 }
0245 
0246 ////////////
0247 
0248 int KCharMacroExpander::expandPlainMacro(const QString &str, int pos, QStringList &ret)
0249 {
0250     if (expandMacro(str.unicode()[pos], ret)) {
0251         return 1;
0252     }
0253     return 0;
0254 }
0255 
0256 int KCharMacroExpander::expandEscapedMacro(const QString &str, int pos, QStringList &ret)
0257 {
0258     if (str.length() <= pos + 1) {
0259         return 0;
0260     }
0261 
0262     if (str.unicode()[pos + 1] == escapeChar()) {
0263         ret += QString(escapeChar());
0264         return 2;
0265     }
0266     if (expandMacro(str.unicode()[pos + 1], ret)) {
0267         return 2;
0268     }
0269     return 0;
0270 }
0271 
0272 int KWordMacroExpander::expandPlainMacro(const QString &str, int pos, QStringList &ret)
0273 {
0274     if (pos && isIdentifier(str.unicode()[pos - 1].unicode())) {
0275         return 0;
0276     }
0277     int sl;
0278     for (sl = 0; isIdentifier(str.unicode()[pos + sl].unicode()); sl++) {
0279         ;
0280     }
0281     if (!sl) {
0282         return 0;
0283     }
0284     if (expandMacro(str.mid(pos, sl), ret)) {
0285         return sl;
0286     }
0287     return 0;
0288 }
0289 
0290 int KWordMacroExpander::expandEscapedMacro(const QString &str, int pos, QStringList &ret)
0291 {
0292     if (str.length() <= pos + 1) {
0293         return 0;
0294     }
0295 
0296     if (str.unicode()[pos + 1] == escapeChar()) {
0297         ret += QString(escapeChar());
0298         return 2;
0299     }
0300     int sl;
0301     int rsl;
0302     int rpos;
0303     if (str.unicode()[pos + 1].unicode() == '{') {
0304         rpos = pos + 2;
0305         if ((sl = str.indexOf(QLatin1Char('}'), rpos)) < 0) {
0306             return 0;
0307         }
0308         sl -= rpos;
0309         rsl = sl + 3;
0310     } else {
0311         rpos = pos + 1;
0312         for (sl = 0; isIdentifier(str.unicode()[rpos + sl].unicode()); ++sl) {
0313             ;
0314         }
0315         rsl = sl + 1;
0316     }
0317     if (!sl) {
0318         return 0;
0319     }
0320     if (expandMacro(str.mid(rpos, sl), ret)) {
0321         return rsl;
0322     }
0323     return 0;
0324 }
0325 
0326 ////////////
0327 
0328 template<typename KT, typename VT>
0329 inline QString TexpandMacros(const QString &ostr, const QHash<KT, VT> &map, QChar c)
0330 {
0331     QString str(ostr);
0332     KMacroMapExpander<KT, VT> kmx(map, c);
0333     kmx.expandMacros(str);
0334     return str;
0335 }
0336 
0337 template<typename KT, typename VT>
0338 inline QString TexpandMacrosShellQuote(const QString &ostr, const QHash<KT, VT> &map, QChar c)
0339 {
0340     QString str(ostr);
0341     KMacroMapExpander<KT, VT> kmx(map, c);
0342     if (!kmx.expandMacrosShellQuote(str)) {
0343         return QString();
0344     }
0345     return str;
0346 }
0347 
0348 // public API
0349 namespace KMacroExpander
0350 {
0351 QString expandMacros(const QString &ostr, const QHash<QChar, QString> &map, QChar c)
0352 {
0353     return TexpandMacros(ostr, map, c);
0354 }
0355 QString expandMacrosShellQuote(const QString &ostr, const QHash<QChar, QString> &map, QChar c)
0356 {
0357     return TexpandMacrosShellQuote(ostr, map, c);
0358 }
0359 QString expandMacros(const QString &ostr, const QHash<QString, QString> &map, QChar c)
0360 {
0361     return TexpandMacros(ostr, map, c);
0362 }
0363 QString expandMacrosShellQuote(const QString &ostr, const QHash<QString, QString> &map, QChar c)
0364 {
0365     return TexpandMacrosShellQuote(ostr, map, c);
0366 }
0367 QString expandMacros(const QString &ostr, const QHash<QChar, QStringList> &map, QChar c)
0368 {
0369     return TexpandMacros(ostr, map, c);
0370 }
0371 QString expandMacrosShellQuote(const QString &ostr, const QHash<QChar, QStringList> &map, QChar c)
0372 {
0373     return TexpandMacrosShellQuote(ostr, map, c);
0374 }
0375 QString expandMacros(const QString &ostr, const QHash<QString, QStringList> &map, QChar c)
0376 {
0377     return TexpandMacros(ostr, map, c);
0378 }
0379 QString expandMacrosShellQuote(const QString &ostr, const QHash<QString, QStringList> &map, QChar c)
0380 {
0381     return TexpandMacrosShellQuote(ostr, map, c);
0382 }
0383 
0384 } // namespace