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 | |
28 | class QString; |
29 | class QStringList; |
30 | template <typename KT, typename VT> class QHash; |
31 | class 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 | */ |
41 | class KDECORE_EXPORT KMacroExpanderBase { |
42 | |
43 | public: |
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 "<tt>case $v in pat)</tt>" syntax. Use |
77 | * "<tt>case $v in (pat)</tt>" 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 | * "<tt>sh -c 'foo \%f'</tt>" is taboo. |
89 | * "<tt>file=\%f sh -c 'foo "$file"'</tt>" 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 - "<tt>for /f ...</tt>" 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 | |
130 | protected: |
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 | |
163 | private: |
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 | */ |
217 | class KDECORE_EXPORT KWordMacroExpander : public KMacroExpanderBase { |
218 | |
219 | public: |
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 | |
226 | protected: |
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 | */ |
253 | class KDECORE_EXPORT KCharMacroExpander : public KMacroExpanderBase { |
254 | |
255 | public: |
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 | |
262 | protected: |
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 | */ |
282 | namespace 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 | |