1/*
2 This file is part of the KDE libraries
3
4 Copyright (c) 2002-2003 Oswald Buddenhagen <ossi@kde.org>
5 Copyright (c) 2003 Waldo Bastian <bastian@kde.org>
6
7 This library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Library General Public
9 License as published by the Free Software Foundation; either
10 version 2 of the License, or (at your option) any later version.
11
12 This library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Library General Public License for more details.
16
17 You should have received a copy of the GNU Library General Public License
18 along with this library; see the file COPYING.LIB. If not, write to
19 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 Boston, MA 02110-1301, USA.
21*/
22#ifndef KMACROEXPANDER_H
23#define KMACROEXPANDER_H
24
25#include <kdecore_export.h>
26#include <QtCore/QChar>
27
28class QString;
29class QStringList;
30template <typename KT, typename VT> class QHash;
31class KMacroExpanderBasePrivate;
32
33/**
34 * \class KMacroExpanderBase kmacroexpander.h <KMacroExpanderBase>
35 *
36 * Abstract base class for the worker classes behind the KMacroExpander namespace
37 * and the KCharMacroExpander and KWordMacroExpander classes.
38 *
39 * @author Oswald Buddenhagen <ossi@kde.org>
40 */
41class KDECORE_EXPORT KMacroExpanderBase {
42
43public:
44 /**
45 * Constructor.
46 * @param c escape char indicating start of macros, or QChar::null for none
47 */
48 explicit KMacroExpanderBase( QChar c = QLatin1Char('%') );
49
50 /**
51 * Destructor.
52 */
53 virtual ~KMacroExpanderBase();
54
55 /**
56 * Perform safe macro expansion (substitution) on a string.
57 *
58 * @param str the string in which macros are expanded in-place
59 */
60 void expandMacros( QString &str );
61
62 // TODO: This documentation is relevant for end-users. Where to put it?
63 /**
64 * Perform safe macro expansion (substitution) on a string for use
65 * in shell commands.
66 *
67 * <h3>*NIX notes</h3>
68 *
69 * Explicitly supported shell constructs:
70 * \ '' "" $'' $"" {} () $(()) ${} $() ``
71 *
72 * Implicitly supported shell constructs:
73 * (())
74 *
75 * Unsupported shell constructs that will cause problems:
76 * Shortened &quot;<tt>case $v in pat)</tt>&quot; syntax. Use
77 * &quot;<tt>case $v in (pat)</tt>&quot; instead.
78 *
79 * The rest of the shell (incl. bash) syntax is simply ignored,
80 * as it is not expected to cause problems.
81 *
82 * Note that bash contains a bug which makes macro expansion within
83 * double quoted substitutions (<tt>"${VAR:-%macro}"</tt>) inherently
84 * insecure.
85 *
86 * For security reasons, @em never put expandos in command line arguments
87 * that are shell commands by themselves -
88 * &quot;<tt>sh -c 'foo \%f'</tt>&quot; is taboo.
89 * &quot;<tt>file=\%f sh -c 'foo "$file"'</tt>&quot; is OK.
90 *
91 * <h3>Windows notes</h3>
92 *
93 * All quoting syntax supported by KShell is supported here as well.
94 * Additionally, command grouping via parentheses is recognized - note
95 * however, that the parser is much stricter about unquoted parentheses
96 * than cmd itself.
97 * The rest of the cmd syntax is simply ignored, as it is not expected
98 * to cause problems - do not use commands that embed other commands,
99 * though - &quot;<tt>for /f ...</tt>&quot; is taboo.
100 *
101 * @param str the string in which macros are expanded in-place
102 * @param pos the position inside the string at which parsing/substitution
103 * should start, and upon exit where processing stopped
104 * @return false if the string could not be parsed and therefore no safe
105 * substitution was possible. Note that macros will have been processed
106 * up to the point where the error occurred. An unmatched closing paren
107 * or brace outside any shell construct is @em not an error (unlike in
108 * the function below), but still prematurely terminates processing.
109 */
110 bool expandMacrosShellQuote( QString &str, int &pos );
111
112 /**
113 * Same as above, but always starts at position 0, and unmatched closing
114 * parens and braces are treated as errors.
115 */
116 bool expandMacrosShellQuote( QString &str );
117
118 /**
119 * Set the macro escape character.
120 * @param c escape char indicating start of macros, or QChar::null if none
121 */
122 void setEscapeChar( QChar c );
123
124 /**
125 * Obtain the macro escape character.
126 * @return escape char indicating start of macros, or QChar::null if none
127 */
128 QChar escapeChar() const;
129
130protected:
131 /**
132 * This function is called for every single char within the string if
133 * the escape char is QChar::null. It should determine whether the
134 * string starting at @p pos within @p str is a valid macro and return
135 * the substitution value for it if so.
136 * @param str the input string
137 * @param pos the offset within @p str
138 * @param ret return value: the string to substitute for the macro
139 * @return If greater than zero, the number of chars at @p pos in @p str
140 * to substitute with @p ret (i.e., a valid macro was found). If less
141 * than zero, subtract this value from @p pos (to skip a macro, i.e.,
142 * substitute it with itself). If zero, no macro starts at @p pos.
143 */
144 virtual int expandPlainMacro( const QString &str, int pos, QStringList &ret );
145
146 /**
147 * This function is called every time the escape char is found if it is
148 * not QChar::null. It should determine whether the
149 * string starting at @p pos witin @p str is a valid macro and return
150 * the substitution value for it if so.
151 * @param str the input string
152 * @param pos the offset within @p str. Note that this is the position of
153 * the occurrence of the escape char
154 * @param ret return value: the string to substitute for the macro
155 * @return If greater than zero, the number of chars at @p pos in @p str
156 * to substitute with @p ret (i.e., a valid macro was found). If less
157 * than zero, subtract this value from @p pos (to skip a macro, i.e.,
158 * substitute it with itself). If zero, scanning continues as if no
159 * escape char was encountered at all.
160 */
161 virtual int expandEscapedMacro( const QString &str, int pos, QStringList &ret );
162
163private:
164 KMacroExpanderBasePrivate * const d;
165};
166
167/**
168 * \class KWordMacroExpander kmacroexpander.h <KMacroExpanderBase>
169 *
170 * Abstract base class for simple word macro substitutors. Use this instead of
171 * the functions in the KMacroExpander namespace if speculatively pre-filling
172 * the substitution map would be too expensive.
173 *
174 * A typical application:
175 *
176 * \code
177 * class MyClass {
178 * ...
179 * private:
180 * QString m_str;
181 * ...
182 * friend class MyExpander;
183 * };
184 *
185 * class MyExpander : public KWordMacroExpander {
186 * public:
187 * MyExpander( MyClass *_that ) : KWordMacroExpander(), that( _that ) {}
188 * protected:
189 * virtual bool expandMacro( const QString &str, QStringList &ret );
190 * private:
191 * MyClass *that;
192 * };
193 *
194 * bool MyExpander::expandMacro( const QString &str, QStringList &ret )
195 * {
196 * if (str == "macro") {
197 * ret += complexOperation( that->m_str );
198 * return true;
199 * }
200 * return false;
201 * }
202 *
203 * ... MyClass::...(...)
204 * {
205 * QString str;
206 * ...
207 * MyExpander mx( this );
208 * mx.expandMacrosShellQuote( str );
209 * ...
210 * }
211 * \endcode
212 *
213 * Alternatively MyClass could inherit from KWordMacroExpander directly.
214 *
215 * @author Oswald Buddenhagen <ossi@kde.org>
216 */
217class KDECORE_EXPORT KWordMacroExpander : public KMacroExpanderBase {
218
219public:
220 /**
221 * Constructor.
222 * @param c escape char indicating start of macros, or QChar::null for none
223 */
224 explicit KWordMacroExpander( QChar c = QLatin1Char('%') ) : KMacroExpanderBase( c ) {}
225
226protected:
227 /** \internal Not to be called or reimplemented. */
228 virtual int expandPlainMacro( const QString &str, int pos, QStringList &ret );
229 /** \internal Not to be called or reimplemented. */
230 virtual int expandEscapedMacro( const QString &str, int pos, QStringList &ret );
231
232 /**
233 * Return substitution list @p ret for string macro @p str.
234 * @param str the macro to expand
235 * @param ret return variable reference. It is guaranteed to be empty
236 * when expandMacro is entered.
237 * @return @c true iff @p chr was a recognized macro name
238 */
239 virtual bool expandMacro( const QString &str, QStringList &ret ) = 0;
240};
241
242/**
243 * \class KCharMacroExpander kmacroexpander.h <KMacroExpanderBase>
244 *
245 * Abstract base class for single char macro substitutors. Use this instead of
246 * the functions in the KMacroExpander namespace if speculatively pre-filling
247 * the substitution map would be too expensive.
248 *
249 * See KWordMacroExpander for a sample application.
250 *
251 * @author Oswald Buddenhagen <ossi@kde.org>
252 */
253class KDECORE_EXPORT KCharMacroExpander : public KMacroExpanderBase {
254
255public:
256 /**
257 * Constructor.
258 * @param c escape char indicating start of macros, or QChar::null for none
259 */
260 explicit KCharMacroExpander( QChar c = QLatin1Char('%') ) : KMacroExpanderBase( c ) {}
261
262protected:
263 /** \internal Not to be called or reimplemented. */
264 virtual int expandPlainMacro( const QString &str, int pos, QStringList &ret );
265 /** \internal Not to be called or reimplemented. */
266 virtual int expandEscapedMacro( const QString &str, int pos, QStringList &ret );
267
268 /**
269 * Return substitution list @p ret for single-character macro @p chr.
270 * @param chr the macro to expand
271 * @param ret return variable reference. It is guaranteed to be empty
272 * when expandMacro is entered.
273 * @return @c true iff @p chr was a recognized macro name
274 */
275 virtual bool expandMacro( QChar chr, QStringList &ret ) = 0;
276};
277
278/**
279 * A group of functions providing macro expansion (substitution) in strings,
280 * optionally with quoting appropriate for shell execution.
281 */
282namespace KMacroExpander {
283 /**
284 * Perform safe macro expansion (substitution) on a string.
285 * The escape char must be quoted with itself to obtain its literal
286 * representation in the resulting string.
287 *
288 * @param str The string to expand
289 * @param map map with substitutions
290 * @param c escape char indicating start of macro, or QChar::null if none
291 * @return the string with all valid macros expanded
292 *
293 * \code
294 * // Code example
295 * QHash<QChar,QString> map;
296 * map.insert('u', "/tmp/myfile.txt");
297 * map.insert('n', "My File");
298 * QString s = "%% Title: %u:%n";
299 * s = KMacroExpander::expandMacros(s, map);
300 * // s is now "% Title: /tmp/myfile.txt:My File";
301 * \endcode
302 */
303 KDECORE_EXPORT QString expandMacros( const QString &str, const QHash<QChar,QString> &map, QChar c = QLatin1Char('%') );
304
305 /**
306 * Perform safe macro expansion (substitution) on a string for use
307 * in shell commands.
308 * The escape char must be quoted with itself to obtain its literal
309 * representation in the resulting string.
310 *
311 * @param str The string to expand
312 * @param map map with substitutions
313 * @param c escape char indicating start of macro, or QChar::null if none
314 * @return the string with all valid macros expanded, or a null string
315 * if a shell syntax error was detected in the command
316 *
317 * \code
318 * // Code example
319 * QHash<QChar,QString> map;
320 * map.insert('u', "/tmp/myfile.txt");
321 * map.insert('n', "My File");
322 * QString s = "kedit --caption %n %u";
323 * s = KMacroExpander::expandMacrosShellQuote(s, map);
324 * // s is now "kedit --caption 'My File' '/tmp/myfile.txt'";
325 * system(QFile::encodeName(s));
326 * \endcode
327 */
328 KDECORE_EXPORT QString expandMacrosShellQuote( const QString &str, const QHash<QChar,QString> &map,
329 QChar c = QLatin1Char('%') );
330
331 /**
332 * Perform safe macro expansion (substitution) on a string.
333 * The escape char must be quoted with itself to obtain its literal
334 * representation in the resulting string.
335 * Macro names can consist of chars in the range [A-Za-z0-9_];
336 * use braces to delimit macros from following words starting
337 * with these chars, or to use other chars for macro names.
338 *
339 * @param str The string to expand
340 * @param map map with substitutions
341 * @param c escape char indicating start of macro, or QChar::null if none
342 * @return the string with all valid macros expanded
343 *
344 * \code
345 * // Code example
346 * QHash<QString,QString> map;
347 * map.insert("url", "/tmp/myfile.txt");
348 * map.insert("name", "My File");
349 * QString s = "Title: %{url}-%name";
350 * s = KMacroExpander::expandMacros(s, map);
351 * // s is now "Title: /tmp/myfile.txt-My File";
352 * \endcode
353 */
354 KDECORE_EXPORT QString expandMacros( const QString &str, const QHash<QString,QString> &map,
355 QChar c = QLatin1Char('%') );
356
357 /**
358 * Perform safe macro expansion (substitution) on a string for use
359 * in shell commands. See KMacroExpanderBase::expandMacrosShellQuote()
360 * for the exact semantics.
361 * The escape char must be quoted with itself to obtain its literal
362 * representation in the resulting string.
363 * Macro names can consist of chars in the range [A-Za-z0-9_];
364 * use braces to delimit macros from following words starting
365 * with these chars, or to use other chars for macro names.
366 *
367 * @param str The string to expand
368 * @param map map with substitutions
369 * @param c escape char indicating start of macro, or QChar::null if none
370 * @return the string with all valid macros expanded, or a null string
371 * if a shell syntax error was detected in the command
372 *
373 * \code
374 * // Code example
375 * QHash<QString,QString> map;
376 * map.insert("url", "/tmp/myfile.txt");
377 * map.insert("name", "My File");
378 * QString s = "kedit --caption %name %{url}";
379 * s = KMacroExpander::expandMacrosShellQuote(s, map);
380 * // s is now "kedit --caption 'My File' '/tmp/myfile.txt'";
381 * system(QFile::encodeName(s));
382 * \endcode
383 */
384 KDECORE_EXPORT QString expandMacrosShellQuote( const QString &str, const QHash<QString,QString> &map,
385 QChar c = QLatin1Char('%') );
386
387 /**
388 * Same as above, except that the macros expand to string lists that
389 * are simply join(" ")ed together.
390 */
391 KDECORE_EXPORT QString expandMacros( const QString &str, const QHash<QChar,QStringList> &map,
392 QChar c = QLatin1Char('%') );
393 KDECORE_EXPORT QString expandMacros( const QString &str, const QHash<QString,QStringList> &map,
394 QChar c = QLatin1Char('%') );
395
396 /**
397 * Same as above, except that the macros expand to string lists.
398 * If the macro appears inside a quoted string, the list is simply
399 * join(" ")ed together; otherwise every element expands to a separate
400 * quoted string.
401 */
402 KDECORE_EXPORT QString expandMacrosShellQuote( const QString &str, const QHash<QChar,QStringList> &map,
403 QChar c = QLatin1Char('%') );
404 KDECORE_EXPORT QString expandMacrosShellQuote( const QString &str, const QHash<QString,QStringList> &map,
405 QChar c = QLatin1Char('%') );
406}
407
408#endif /* KMACROEXPANDER_H */
409