File indexing completed on 2024-04-28 03:53:48

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 ////////
0105 
0106 static bool isIdentifier(ushort c)
0107 {
0108     return c == '_' || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9');
0109 }
0110 
0111 ////////
0112 
0113 template<typename VT>
0114 class KMacroMapExpander<QChar, VT> : public KMacroExpanderBase
0115 {
0116 public:
0117     KMacroMapExpander(const QHash<QChar, VT> &map, QChar c = QLatin1Char('%'))
0118         : KMacroExpanderBase(c)
0119         , macromap(map)
0120     {
0121     }
0122 
0123 protected:
0124     int expandPlainMacro(const QString &str, int pos, QStringList &ret) override;
0125     int expandEscapedMacro(const QString &str, int pos, QStringList &ret) override;
0126 
0127 private:
0128     QHash<QChar, VT> macromap;
0129 };
0130 
0131 template<typename VT>
0132 int KMacroMapExpander<QChar, VT>::expandPlainMacro(const QString &str, int pos, QStringList &ret)
0133 {
0134     typename QHash<QChar, VT>::const_iterator it = macromap.constFind(str.unicode()[pos]);
0135     if (it != macromap.constEnd()) {
0136         ret += it.value();
0137         return 1;
0138     }
0139     return 0;
0140 }
0141 
0142 template<typename VT>
0143 int KMacroMapExpander<QChar, VT>::expandEscapedMacro(const QString &str, int pos, QStringList &ret)
0144 {
0145     if (str.length() <= pos + 1) {
0146         return 0;
0147     }
0148 
0149     if (str.unicode()[pos + 1] == escapeChar()) {
0150         ret += QString(escapeChar());
0151         return 2;
0152     }
0153     typename QHash<QChar, VT>::const_iterator it = macromap.constFind(str.unicode()[pos + 1]);
0154     if (it != macromap.constEnd()) {
0155         ret += it.value();
0156         return 2;
0157     }
0158 
0159     return 0;
0160 }
0161 
0162 template<typename VT>
0163 class KMacroMapExpander<QString, VT> : public KMacroExpanderBase
0164 {
0165 public:
0166     KMacroMapExpander(const QHash<QString, VT> &map, QChar c = QLatin1Char('%'))
0167         : KMacroExpanderBase(c)
0168         , macromap(map)
0169     {
0170     }
0171 
0172 protected:
0173     int expandPlainMacro(const QString &str, int pos, QStringList &ret) override;
0174     int expandEscapedMacro(const QString &str, int pos, QStringList &ret) override;
0175 
0176 private:
0177     QHash<QString, VT> macromap;
0178 };
0179 
0180 template<typename VT>
0181 int KMacroMapExpander<QString, VT>::expandPlainMacro(const QString &str, int pos, QStringList &ret)
0182 {
0183     if (pos && isIdentifier(str.unicode()[pos - 1].unicode())) {
0184         return 0;
0185     }
0186     int sl;
0187     for (sl = 0; isIdentifier(str.unicode()[pos + sl].unicode()); sl++) {
0188         ;
0189     }
0190     if (!sl) {
0191         return 0;
0192     }
0193     typename QHash<QString, VT>::const_iterator it = macromap.constFind(str.mid(pos, sl));
0194     if (it != macromap.constEnd()) {
0195         ret += it.value();
0196         return sl;
0197     }
0198     return 0;
0199 }
0200 
0201 template<typename VT>
0202 int KMacroMapExpander<QString, VT>::expandEscapedMacro(const QString &str, int pos, QStringList &ret)
0203 {
0204     if (str.length() <= pos + 1) {
0205         return 0;
0206     }
0207 
0208     if (str.unicode()[pos + 1] == escapeChar()) {
0209         ret += QString(escapeChar());
0210         return 2;
0211     }
0212     int sl;
0213     int rsl;
0214     int rpos;
0215     if (str.unicode()[pos + 1].unicode() == '{') {
0216         rpos = pos + 2;
0217         if ((sl = str.indexOf(QLatin1Char('}'), rpos)) < 0) {
0218             return 0;
0219         }
0220         sl -= rpos;
0221         rsl = sl + 3;
0222     } else {
0223         rpos = pos + 1;
0224         for (sl = 0; isIdentifier(str.unicode()[rpos + sl].unicode()); ++sl) {
0225             ;
0226         }
0227         rsl = sl + 1;
0228     }
0229     if (!sl) {
0230         return 0;
0231     }
0232     typename QHash<QString, VT>::const_iterator it = macromap.constFind(str.mid(rpos, sl));
0233     if (it != macromap.constEnd()) {
0234         ret += it.value();
0235         return rsl;
0236     }
0237     return 0;
0238 }
0239 
0240 ////////////
0241 
0242 int KCharMacroExpander::expandPlainMacro(const QString &str, int pos, QStringList &ret)
0243 {
0244     if (expandMacro(str.unicode()[pos], ret)) {
0245         return 1;
0246     }
0247     return 0;
0248 }
0249 
0250 int KCharMacroExpander::expandEscapedMacro(const QString &str, int pos, QStringList &ret)
0251 {
0252     if (str.length() <= pos + 1) {
0253         return 0;
0254     }
0255 
0256     if (str.unicode()[pos + 1] == escapeChar()) {
0257         ret += QString(escapeChar());
0258         return 2;
0259     }
0260     if (expandMacro(str.unicode()[pos + 1], ret)) {
0261         return 2;
0262     }
0263     return 0;
0264 }
0265 
0266 int KWordMacroExpander::expandPlainMacro(const QString &str, int pos, QStringList &ret)
0267 {
0268     if (pos && isIdentifier(str.unicode()[pos - 1].unicode())) {
0269         return 0;
0270     }
0271     int sl;
0272     for (sl = 0; isIdentifier(str.unicode()[pos + sl].unicode()); sl++) {
0273         ;
0274     }
0275     if (!sl) {
0276         return 0;
0277     }
0278     if (expandMacro(str.mid(pos, sl), ret)) {
0279         return sl;
0280     }
0281     return 0;
0282 }
0283 
0284 int KWordMacroExpander::expandEscapedMacro(const QString &str, int pos, QStringList &ret)
0285 {
0286     if (str.length() <= pos + 1) {
0287         return 0;
0288     }
0289 
0290     if (str.unicode()[pos + 1] == escapeChar()) {
0291         ret += QString(escapeChar());
0292         return 2;
0293     }
0294     int sl;
0295     int rsl;
0296     int rpos;
0297     if (str.unicode()[pos + 1].unicode() == '{') {
0298         rpos = pos + 2;
0299         if ((sl = str.indexOf(QLatin1Char('}'), rpos)) < 0) {
0300             return 0;
0301         }
0302         sl -= rpos;
0303         rsl = sl + 3;
0304     } else {
0305         rpos = pos + 1;
0306         for (sl = 0; isIdentifier(str.unicode()[rpos + sl].unicode()); ++sl) {
0307             ;
0308         }
0309         rsl = sl + 1;
0310     }
0311     if (!sl) {
0312         return 0;
0313     }
0314     if (expandMacro(str.mid(rpos, sl), ret)) {
0315         return rsl;
0316     }
0317     return 0;
0318 }
0319 
0320 ////////////
0321 
0322 template<typename KT, typename VT>
0323 inline QString TexpandMacros(const QString &ostr, const QHash<KT, VT> &map, QChar c)
0324 {
0325     QString str(ostr);
0326     KMacroMapExpander<KT, VT> kmx(map, c);
0327     kmx.expandMacros(str);
0328     return str;
0329 }
0330 
0331 template<typename KT, typename VT>
0332 inline QString TexpandMacrosShellQuote(const QString &ostr, const QHash<KT, VT> &map, QChar c)
0333 {
0334     QString str(ostr);
0335     KMacroMapExpander<KT, VT> kmx(map, c);
0336     if (!kmx.expandMacrosShellQuote(str)) {
0337         return QString();
0338     }
0339     return str;
0340 }
0341 
0342 // public API
0343 namespace KMacroExpander
0344 {
0345 QString expandMacros(const QString &ostr, const QHash<QChar, QString> &map, QChar c)
0346 {
0347     return TexpandMacros(ostr, map, c);
0348 }
0349 QString expandMacrosShellQuote(const QString &ostr, const QHash<QChar, QString> &map, QChar c)
0350 {
0351     return TexpandMacrosShellQuote(ostr, map, c);
0352 }
0353 QString expandMacros(const QString &ostr, const QHash<QString, QString> &map, QChar c)
0354 {
0355     return TexpandMacros(ostr, map, c);
0356 }
0357 QString expandMacrosShellQuote(const QString &ostr, const QHash<QString, QString> &map, QChar c)
0358 {
0359     return TexpandMacrosShellQuote(ostr, map, c);
0360 }
0361 QString expandMacros(const QString &ostr, const QHash<QChar, QStringList> &map, QChar c)
0362 {
0363     return TexpandMacros(ostr, map, c);
0364 }
0365 QString expandMacrosShellQuote(const QString &ostr, const QHash<QChar, QStringList> &map, QChar c)
0366 {
0367     return TexpandMacrosShellQuote(ostr, map, c);
0368 }
0369 QString expandMacros(const QString &ostr, const QHash<QString, QStringList> &map, QChar c)
0370 {
0371     return TexpandMacros(ostr, map, c);
0372 }
0373 QString expandMacrosShellQuote(const QString &ostr, const QHash<QString, QStringList> &map, QChar c)
0374 {
0375     return TexpandMacrosShellQuote(ostr, map, c);
0376 }
0377 
0378 } // namespace