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 #ifndef KMACROEXPANDER_H 0010 #define KMACROEXPANDER_H 0011 0012 #include <QChar> 0013 #include <QStringList> 0014 0015 #include <kcoreaddons_export.h> 0016 #include <memory> 0017 0018 class QString; 0019 template<typename KT, typename VT> 0020 class QHash; 0021 class KMacroExpanderBasePrivate; 0022 0023 /** 0024 * \class KMacroExpanderBase kmacroexpander.h <KMacroExpander> 0025 * 0026 * Abstract base class for the worker classes behind the KMacroExpander namespace 0027 * and the KCharMacroExpander and KWordMacroExpander classes. 0028 * 0029 * @author Oswald Buddenhagen <ossi@kde.org> 0030 */ 0031 class KCOREADDONS_EXPORT KMacroExpanderBase 0032 { 0033 public: 0034 /** 0035 * Constructor. 0036 * @param c escape char indicating start of macros, or QChar::null for none 0037 */ 0038 explicit KMacroExpanderBase(QChar c = QLatin1Char('%')); 0039 0040 /** 0041 * Destructor. 0042 */ 0043 virtual ~KMacroExpanderBase(); 0044 0045 /** 0046 * Perform safe macro expansion (substitution) on a string. 0047 * 0048 * @param str the string in which macros are expanded in-place 0049 */ 0050 void expandMacros(QString &str); 0051 0052 // TODO: This documentation is relevant for end-users. Where to put it? 0053 /** 0054 * Perform safe macro expansion (substitution) on a string for use 0055 * in shell commands. 0056 * 0057 * <h3>*NIX notes</h3> 0058 * 0059 * Explicitly supported shell constructs: 0060 * \ '' "" $'' $"" {} () $(()) ${} $() `` 0061 * 0062 * Implicitly supported shell constructs: 0063 * (()) 0064 * 0065 * Unsupported shell constructs that will cause problems: 0066 * Shortened "<tt>case $v in pat)</tt>" syntax. Use 0067 * "<tt>case $v in (pat)</tt>" instead. 0068 * 0069 * The rest of the shell (incl. bash) syntax is simply ignored, 0070 * as it is not expected to cause problems. 0071 * 0072 * Note that bash contains a bug which makes macro expansion within 0073 * double quoted substitutions (<tt>"${VAR:-%macro}"</tt>) inherently 0074 * insecure. 0075 * 0076 * For security reasons, @em never put expandos in command line arguments 0077 * that are shell commands by themselves - 0078 * "<tt>sh -c 'foo \%f'</tt>" is taboo. 0079 * "<tt>file=\%f sh -c 'foo "$file"'</tt>" is OK. 0080 * 0081 * <h3>Windows notes</h3> 0082 * 0083 * All quoting syntax supported by KShell is supported here as well. 0084 * Additionally, command grouping via parentheses is recognized - note 0085 * however, that the parser is much stricter about unquoted parentheses 0086 * than cmd itself. 0087 * The rest of the cmd syntax is simply ignored, as it is not expected 0088 * to cause problems - do not use commands that embed other commands, 0089 * though - "<tt>for /f ...</tt>" is taboo. 0090 * 0091 * @param str the string in which macros are expanded in-place 0092 * @param pos the position inside the string at which parsing/substitution 0093 * should start, and upon exit where processing stopped 0094 * @return false if the string could not be parsed and therefore no safe 0095 * substitution was possible. Note that macros will have been processed 0096 * up to the point where the error occurred. An unmatched closing paren 0097 * or brace outside any shell construct is @em not an error (unlike in 0098 * the function below), but still prematurely terminates processing. 0099 */ 0100 bool expandMacrosShellQuote(QString &str, int &pos); 0101 0102 /** 0103 * Same as above, but always starts at position 0, and unmatched closing 0104 * parens and braces are treated as errors. 0105 */ 0106 bool expandMacrosShellQuote(QString &str); 0107 0108 /** 0109 * Set the macro escape character. 0110 * @param c escape char indicating start of macros, or QChar::null if none 0111 */ 0112 void setEscapeChar(QChar c); 0113 0114 /** 0115 * Obtain the macro escape character. 0116 * @return escape char indicating start of macros, or QChar::null if none 0117 */ 0118 QChar escapeChar() const; 0119 0120 protected: 0121 /** 0122 * This function is called for every single char within the string if 0123 * the escape char is QChar::null. It should determine whether the 0124 * string starting at @p pos within @p str is a valid macro and return 0125 * the substitution value for it if so. 0126 * @param str the input string 0127 * @param pos the offset within @p str 0128 * @param ret return value: the string to substitute for the macro 0129 * @return If greater than zero, the number of chars at @p pos in @p str 0130 * to substitute with @p ret (i.e., a valid macro was found). If less 0131 * than zero, subtract this value from @p pos (to skip a macro, i.e., 0132 * substitute it with itself). If zero, no macro starts at @p pos. 0133 */ 0134 virtual int expandPlainMacro(const QString &str, int pos, QStringList &ret); 0135 0136 /** 0137 * This function is called every time the escape char is found if it is 0138 * not QChar::null. It should determine whether the 0139 * string starting at @p pos witin @p str is a valid macro and return 0140 * the substitution value for it if so. 0141 * @param str the input string 0142 * @param pos the offset within @p str. Note that this is the position of 0143 * the occurrence of the escape char 0144 * @param ret return value: the string to substitute for the macro 0145 * @return If greater than zero, the number of chars at @p pos in @p str 0146 * to substitute with @p ret (i.e., a valid macro was found). If less 0147 * than zero, subtract this value from @p pos (to skip a macro, i.e., 0148 * substitute it with itself). If zero, scanning continues as if no 0149 * escape char was encountered at all. 0150 */ 0151 virtual int expandEscapedMacro(const QString &str, int pos, QStringList &ret); 0152 0153 private: 0154 std::unique_ptr<KMacroExpanderBasePrivate> const d; 0155 }; 0156 0157 /** 0158 * \class KWordMacroExpander kmacroexpander.h <KMacroExpanderBase> 0159 * 0160 * Abstract base class for simple word macro substitutors. Use this instead of 0161 * the functions in the KMacroExpander namespace if speculatively pre-filling 0162 * the substitution map would be too expensive. 0163 * 0164 * A typical application: 0165 * 0166 * \code 0167 * class MyClass { 0168 * ... 0169 * private: 0170 * QString m_str; 0171 * ... 0172 * friend class MyExpander; 0173 * }; 0174 * 0175 * class MyExpander : public KWordMacroExpander { 0176 * public: 0177 * MyExpander( MyClass *_that ) : KWordMacroExpander(), that( _that ) {} 0178 * protected: 0179 * virtual bool expandMacro( const QString &str, QStringList &ret ); 0180 * private: 0181 * MyClass *that; 0182 * }; 0183 * 0184 * bool MyExpander::expandMacro( const QString &str, QStringList &ret ) 0185 * { 0186 * if (str == "macro") { 0187 * ret += complexOperation( that->m_str ); 0188 * return true; 0189 * } 0190 * return false; 0191 * } 0192 * 0193 * ... MyClass::...(...) 0194 * { 0195 * QString str; 0196 * ... 0197 * MyExpander mx( this ); 0198 * mx.expandMacrosShellQuote( str ); 0199 * ... 0200 * } 0201 * \endcode 0202 * 0203 * Alternatively MyClass could inherit from KWordMacroExpander directly. 0204 * 0205 * @author Oswald Buddenhagen <ossi@kde.org> 0206 */ 0207 class KCOREADDONS_EXPORT KWordMacroExpander : public KMacroExpanderBase 0208 { 0209 public: 0210 /** 0211 * Constructor. 0212 * @param c escape char indicating start of macros, or QChar::null for none 0213 */ 0214 explicit KWordMacroExpander(QChar c = QLatin1Char('%')) 0215 : KMacroExpanderBase(c) 0216 { 0217 } 0218 0219 protected: 0220 /** \internal Not to be called or reimplemented. */ 0221 int expandPlainMacro(const QString &str, int pos, QStringList &ret) override; 0222 /** \internal Not to be called or reimplemented. */ 0223 int expandEscapedMacro(const QString &str, int pos, QStringList &ret) override; 0224 0225 /** 0226 * Return substitution list @p ret for string macro @p str. 0227 * @param str the macro to expand 0228 * @param ret return variable reference. It is guaranteed to be empty 0229 * when expandMacro is entered. 0230 * @return @c true iff @p str was a recognized macro name 0231 */ 0232 virtual bool expandMacro(const QString &str, QStringList &ret) = 0; 0233 }; 0234 0235 /** 0236 * \class KCharMacroExpander kmacroexpander.h <KMacroExpanderBase> 0237 * 0238 * Abstract base class for single char macro substitutors. Use this instead of 0239 * the functions in the KMacroExpander namespace if speculatively pre-filling 0240 * the substitution map would be too expensive. 0241 * 0242 * See KWordMacroExpander for a sample application. 0243 * 0244 * @author Oswald Buddenhagen <ossi@kde.org> 0245 */ 0246 class KCOREADDONS_EXPORT KCharMacroExpander : public KMacroExpanderBase 0247 { 0248 public: 0249 /** 0250 * Constructor. 0251 * @param c escape char indicating start of macros, or QChar::null for none 0252 */ 0253 explicit KCharMacroExpander(QChar c = QLatin1Char('%')) 0254 : KMacroExpanderBase(c) 0255 { 0256 } 0257 0258 protected: 0259 /** \internal Not to be called or reimplemented. */ 0260 int expandPlainMacro(const QString &str, int pos, QStringList &ret) override; 0261 /** \internal Not to be called or reimplemented. */ 0262 int expandEscapedMacro(const QString &str, int pos, QStringList &ret) override; 0263 0264 /** 0265 * Return substitution list @p ret for single-character macro @p chr. 0266 * @param chr the macro to expand 0267 * @param ret return variable reference. It is guaranteed to be empty 0268 * when expandMacro is entered. 0269 * @return @c true iff @p chr was a recognized macro name 0270 */ 0271 virtual bool expandMacro(QChar chr, QStringList &ret) = 0; 0272 }; 0273 0274 /** 0275 * A group of functions providing macro expansion (substitution) in strings, 0276 * optionally with quoting appropriate for shell execution. 0277 */ 0278 namespace KMacroExpander 0279 { 0280 /** 0281 * Perform safe macro expansion (substitution) on a string. 0282 * The escape char must be quoted with itself to obtain its literal 0283 * representation in the resulting string. 0284 * 0285 * @param str The string to expand 0286 * @param map map with substitutions 0287 * @param c escape char indicating start of macro, or QChar::null if none 0288 * @return the string with all valid macros expanded 0289 * 0290 * \code 0291 * // Code example 0292 * QHash<QChar,QString> map; 0293 * map.insert('u', "/tmp/myfile.txt"); 0294 * map.insert('n', "My File"); 0295 * QString s = "%% Title: %u:%n"; 0296 * s = KMacroExpander::expandMacros(s, map); 0297 * // s is now "% Title: /tmp/myfile.txt:My File"; 0298 * \endcode 0299 */ 0300 KCOREADDONS_EXPORT QString expandMacros(const QString &str, const QHash<QChar, QString> &map, QChar c = QLatin1Char('%')); 0301 0302 /** 0303 * Perform safe macro expansion (substitution) on a string for use 0304 * in shell commands. 0305 * The escape char must be quoted with itself to obtain its literal 0306 * representation in the resulting string. 0307 * 0308 * @param str The string to expand 0309 * @param map map with substitutions 0310 * @param c escape char indicating start of macro, or QChar::null if none 0311 * @return the string with all valid macros expanded, or a null string 0312 * if a shell syntax error was detected in the command 0313 * 0314 * \code 0315 * // Code example 0316 * QHash<QChar,QString> map; 0317 * map.insert('u', "/tmp/myfile.txt"); 0318 * map.insert('n', "My File"); 0319 * QString s = "kwrite --qwindowtitle %n %u"; 0320 * s = KMacroExpander::expandMacrosShellQuote(s, map); 0321 * // s is now "kwrite --qwindowtitle 'My File' '/tmp/myfile.txt'"; 0322 * system(QFile::encodeName(s)); 0323 * \endcode 0324 */ 0325 KCOREADDONS_EXPORT QString expandMacrosShellQuote(const QString &str, const QHash<QChar, QString> &map, QChar c = QLatin1Char('%')); 0326 0327 /** 0328 * Perform safe macro expansion (substitution) on a string. 0329 * The escape char must be quoted with itself to obtain its literal 0330 * representation in the resulting string. 0331 * Macro names can consist of chars in the range [A-Za-z0-9_]; 0332 * use braces to delimit macros from following words starting 0333 * with these chars, or to use other chars for macro names. 0334 * 0335 * @param str The string to expand 0336 * @param map map with substitutions 0337 * @param c escape char indicating start of macro, or QChar::null if none 0338 * @return the string with all valid macros expanded 0339 * 0340 * \code 0341 * // Code example 0342 * QHash<QString,QString> map; 0343 * map.insert("url", "/tmp/myfile.txt"); 0344 * map.insert("name", "My File"); 0345 * QString s = "Title: %{url}-%name"; 0346 * s = KMacroExpander::expandMacros(s, map); 0347 * // s is now "Title: /tmp/myfile.txt-My File"; 0348 * \endcode 0349 */ 0350 KCOREADDONS_EXPORT QString expandMacros(const QString &str, const QHash<QString, QString> &map, QChar c = QLatin1Char('%')); 0351 0352 /** 0353 * Perform safe macro expansion (substitution) on a string for use 0354 * in shell commands. See KMacroExpanderBase::expandMacrosShellQuote() 0355 * for the exact semantics. 0356 * The escape char must be quoted with itself to obtain its literal 0357 * representation in the resulting string. 0358 * Macro names can consist of chars in the range [A-Za-z0-9_]; 0359 * use braces to delimit macros from following words starting 0360 * with these chars, or to use other chars for macro names. 0361 * 0362 * @param str The string to expand 0363 * @param map map with substitutions 0364 * @param c escape char indicating start of macro, or QChar::null if none 0365 * @return the string with all valid macros expanded, or a null string 0366 * if a shell syntax error was detected in the command 0367 * 0368 * \code 0369 * // Code example 0370 * QHash<QString,QString> map; 0371 * map.insert("url", "/tmp/myfile.txt"); 0372 * map.insert("name", "My File"); 0373 * QString s = "kwrite --qwindowtitle %name %{url}"; 0374 * s = KMacroExpander::expandMacrosShellQuote(s, map); 0375 * // s is now "kwrite --qwindowtitle 'My File' '/tmp/myfile.txt'"; 0376 * system(QFile::encodeName(s)); 0377 * \endcode 0378 */ 0379 KCOREADDONS_EXPORT QString expandMacrosShellQuote(const QString &str, const QHash<QString, QString> &map, QChar c = QLatin1Char('%')); 0380 0381 /** 0382 * Same as above, except that the macros expand to string lists that 0383 * are simply join(" ")ed together. 0384 */ 0385 KCOREADDONS_EXPORT QString expandMacros(const QString &str, const QHash<QChar, QStringList> &map, QChar c = QLatin1Char('%')); 0386 KCOREADDONS_EXPORT QString expandMacros(const QString &str, const QHash<QString, QStringList> &map, QChar c = QLatin1Char('%')); 0387 0388 /** 0389 * Same as above, except that the macros expand to string lists. 0390 * If the macro appears inside a quoted string, the list is simply 0391 * join(" ")ed together; otherwise every element expands to a separate 0392 * quoted string. 0393 */ 0394 KCOREADDONS_EXPORT QString expandMacrosShellQuote(const QString &str, const QHash<QChar, QStringList> &map, QChar c = QLatin1Char('%')); 0395 KCOREADDONS_EXPORT QString expandMacrosShellQuote(const QString &str, const QHash<QString, QStringList> &map, QChar c = QLatin1Char('%')); 0396 } 0397 0398 #endif /* KMACROEXPANDER_H */