kdecore Library API Documentation

kmacroexpander.cpp

00001 /* 00002 This file is part of the KDE libraries 00003 00004 Copyright (c) 2002-2003 Oswald Buddenhagen <ossi@kde.org> 00005 Copyright (c) 2003 Waldo Bastian <bastian@kde.org> 00006 00007 This library is free software; you can redistribute it and/or 00008 modify it under the terms of the GNU Library General Public 00009 License as published by the Free Software Foundation; either 00010 version 2 of the License, or (at your option) any later version. 00011 00012 This library is distributed in the hope that it will be useful, 00013 but WITHOUT ANY WARRANTY; without even the implied warranty of 00014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00015 Library General Public License for more details. 00016 00017 You should have received a copy of the GNU Library General Public License 00018 along with this library; see the file COPYING.LIB. If not, write to 00019 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, 00020 Boston, MA 02111-1307, USA. 00021 */ 00022 00023 #include <kmacroexpander.h> 00024 00025 #include <qvaluestack.h> 00026 #include <qregexp.h> 00027 00028 KMacroExpanderBase::KMacroExpanderBase( QChar c ) 00029 { 00030 escapechar = c; 00031 } 00032 00033 KMacroExpanderBase::~KMacroExpanderBase() 00034 { 00035 } 00036 00037 void 00038 KMacroExpanderBase::setEscapeChar( QChar c ) 00039 { 00040 escapechar = c; 00041 } 00042 00043 QChar 00044 KMacroExpanderBase::escapeChar() const 00045 { 00046 return escapechar; 00047 } 00048 00049 void KMacroExpanderBase::expandMacros( QString &str ) 00050 { 00051 uint pos; 00052 int len; 00053 QChar ec( escapechar ); 00054 QStringList rst; 00055 QString rsts; 00056 00057 for (pos = 0; pos < str.length(); ) { 00058 if (ec != (char)0) { 00059 if (str.unicode()[pos] != ec) 00060 goto nohit; 00061 if (!(len = expandEscapedMacro( str, pos, rst ))) 00062 goto nohit; 00063 } else { 00064 if (!(len = expandPlainMacro( str, pos, rst ))) 00065 goto nohit; 00066 } 00067 if (len < 0) { 00068 pos -= len; 00069 continue; 00070 } 00071 rsts = rst.join( " " ); 00072 rst.clear(); 00073 str.replace( pos, len, rsts ); 00074 pos += rsts.length(); 00075 continue; 00076 nohit: 00077 pos++; 00078 } 00079 } 00080 00081 00082 namespace KMacroExpander { 00083 00084 enum Quoting { noquote, singlequote, doublequote, dollarquote, 00085 paren, subst, group, math }; 00086 typedef struct { 00087 Quoting current; 00088 bool dquote; 00089 } State; 00090 typedef struct { 00091 QString str; 00092 uint pos; 00093 } Save; 00094 00095 } 00096 00097 using namespace KMacroExpander; 00098 00099 bool KMacroExpanderBase::expandMacrosShellQuote( QString &str, uint &pos ) 00100 { 00101 int len; 00102 uint pos2; 00103 QChar ec( escapechar ); 00104 State state = { noquote, false }; 00105 QValueStack<State> sstack; 00106 QValueStack<Save> ostack; 00107 QStringList rst; 00108 QString rsts; 00109 00110 while (pos < str.length()) { 00111 QChar cc( str.unicode()[pos] ); 00112 if (ec != (char)0) { 00113 if (cc != ec) 00114 goto nohit; 00115 if (!(len = expandEscapedMacro( str, pos, rst ))) 00116 goto nohit; 00117 } else { 00118 if (!(len = expandPlainMacro( str, pos, rst ))) 00119 goto nohit; 00120 } 00121 if (len < 0) { 00122 pos -= len; 00123 continue; 00124 } 00125 if (state.dquote) { 00126 rsts = rst.join( " " ); 00127 rsts.replace( QRegExp("([$`\"\\\\])"), "\\\\1" ); 00128 } else if (state.current == dollarquote) { 00129 rsts = rst.join( " " ); 00130 rsts.replace( QRegExp("(['\\\\])"), "\\\\1" ); 00131 } else if (state.current == singlequote) { 00132 rsts = rst.join( " " ); 00133 rsts.replace( '\'', "'\\''"); 00134 } else { 00135 if (rst.isEmpty()) { 00136 str.remove( pos, len ); 00137 continue; 00138 } else { 00139 rsts = "'"; 00140 #if 0 // this could pay off if join() would be cleverer and the strings were long 00141 for (QStringList::Iterator it = rst.begin(); it != rst.end(); ++it) 00142 (*it).replace( '\'', "'\\''" ); 00143 rsts += rst.join( "' '" ); 00144 #else 00145 for (QStringList::ConstIterator it = rst.begin(); it != rst.end(); ++it) { 00146 if (it != rst.begin()) 00147 rsts += "' '"; 00148 QString trsts( *it ); 00149 trsts.replace( '\'', "'\\''" ); 00150 rsts += trsts; 00151 } 00152 #endif 00153 rsts += "'"; 00154 } 00155 } 00156 rst.clear(); 00157 str.replace( pos, len, rsts ); 00158 pos += rsts.length(); 00159 continue; 00160 nohit: 00161 if (state.current == singlequote) { 00162 if (cc == '\'') 00163 state = sstack.pop(); 00164 } else if (cc == '\\') { 00165 // always swallow the char -> prevent anomalies due to expansion 00166 pos += 2; 00167 continue; 00168 } else if (state.current == dollarquote) { 00169 if (cc == '\'') 00170 state = sstack.pop(); 00171 } else if (cc == '$') { 00172 cc = str[++pos]; 00173 if (cc == '(') { 00174 sstack.push( state ); 00175 if (str[pos + 1] == '(') { 00176 Save sav = { str, pos + 2 }; 00177 ostack.push( sav ); 00178 state.current = math; 00179 pos += 2; 00180 continue; 00181 } else { 00182 state.current = paren; 00183 state.dquote = false; 00184 } 00185 } else if (cc == '{') { 00186 sstack.push( state ); 00187 state.current = subst; 00188 } else if (!state.dquote) { 00189 if (cc == '\'') { 00190 sstack.push( state ); 00191 state.current = dollarquote; 00192 } else if (cc == '"') { 00193 sstack.push( state ); 00194 state.current = doublequote; 00195 state.dquote = true; 00196 } 00197 } 00198 // always swallow the char -> prevent anomalies due to expansion 00199 } else if (cc == '`') { 00200 str.replace( pos, 1, "$( " ); // add space -> avoid creating $(( 00201 pos2 = pos += 3; 00202 for (;;) { 00203 if (pos2 >= str.length()) { 00204 pos = pos2; 00205 return false; 00206 } 00207 cc = str.unicode()[pos2]; 00208 if (cc == '`') 00209 break; 00210 if (cc == '\\') { 00211 cc = str[++pos2]; 00212 if (cc == '$' || cc == '`' || cc == '\\' || 00213 (cc == '"' && state.dquote)) 00214 { 00215 str.remove( pos2 - 1, 1 ); 00216 continue; 00217 } 00218 } 00219 pos2++; 00220 } 00221 str[pos2] = ')'; 00222 sstack.push( state ); 00223 state.current = paren; 00224 state.dquote = false; 00225 continue; 00226 } else if (state.current == doublequote) { 00227 if (cc == '"') 00228 state = sstack.pop(); 00229 } else if (cc == '\'') { 00230 if (!state.dquote) { 00231 sstack.push( state ); 00232 state.current = singlequote; 00233 } 00234 } else if (cc == '"') { 00235 if (!state.dquote) { 00236 sstack.push( state ); 00237 state.current = doublequote; 00238 state.dquote = true; 00239 } 00240 } else if (state.current == subst) { 00241 if (cc == '}') 00242 state = sstack.pop(); 00243 } else if (cc == ')') { 00244 if (state.current == math) { 00245 if (str[pos + 1] == ')') { 00246 state = sstack.pop(); 00247 pos += 2; 00248 } else { 00249 // false hit: the $(( was a $( ( in fact 00250 // ash does not care, but bash does 00251 pos = ostack.top().pos; 00252 str = ostack.top().str; 00253 ostack.pop(); 00254 state.current = paren; 00255 state.dquote = false; 00256 sstack.push( state ); 00257 } 00258 continue; 00259 } else if (state.current == paren) 00260 state = sstack.pop(); 00261 else 00262 break; 00263 } else if (cc == '}') { 00264 if (state.current == KMacroExpander::group) 00265 state = sstack.pop(); 00266 else 00267 break; 00268 } else if (cc == '(') { 00269 sstack.push( state ); 00270 state.current = paren; 00271 } else if (cc == '{') { 00272 sstack.push( state ); 00273 state.current = KMacroExpander::group; 00274 } 00275 pos++; 00276 } 00277 return sstack.empty(); 00278 } 00279 00280 bool KMacroExpanderBase::expandMacrosShellQuote( QString &str ) 00281 { 00282 uint pos = 0; 00283 return expandMacrosShellQuote( str, pos ) && pos == str.length(); 00284 } 00285 00286 int KMacroExpanderBase::expandPlainMacro( const QString &, uint, QStringList & ) 00287 { qFatal( "KMacroExpanderBase::expandPlainMacro called!" ); return 0; } 00288 00289 int KMacroExpanderBase::expandEscapedMacro( const QString &, uint, QStringList & ) 00290 { qFatal( "KMacroExpanderBase::expandEscapedMacro called!" ); return 0; } 00291 00292 00294 00295 template<class KT,class VT> 00296 class KMacroMapExpander : public KMacroExpanderBase { 00297 00298 public: 00299 KMacroMapExpander( const QMap<KT,VT> &map, QChar c = '%' ) : 00300 KMacroExpanderBase( c ), macromap( map ) {} 00301 00302 protected: 00303 virtual int expandPlainMacro( const QString &str, uint pos, QStringList &ret ); 00304 virtual int expandEscapedMacro( const QString &str, uint pos, QStringList &ret ); 00305 00306 private: 00307 QMap<KT,VT> macromap; 00308 }; 00309 00310 static QStringList &operator+=( QStringList &s, const QString &n) { s << n; return s; } 00311 00313 00314 template<class VT> 00315 class KMacroMapExpander<QChar,VT> : public KMacroExpanderBase { 00316 00317 public: 00318 KMacroMapExpander( const QMap<QChar,VT> &map, QChar c = '%' ) : 00319 KMacroExpanderBase( c ), macromap( map ) {} 00320 00321 protected: 00322 virtual int expandPlainMacro( const QString &str, uint pos, QStringList &ret ); 00323 virtual int expandEscapedMacro( const QString &str, uint pos, QStringList &ret ); 00324 00325 private: 00326 QMap<QChar,VT> macromap; 00327 }; 00328 00329 template<class VT> 00330 int 00331 KMacroMapExpander<QChar,VT>::expandPlainMacro( const QString &str, uint pos, QStringList &ret ) 00332 { 00333 QMapConstIterator<QChar,VT> it = macromap.find(str[pos]); 00334 if (it != macromap.end()) { 00335 ret += it.data(); 00336 return 1; 00337 } 00338 return 0; 00339 } 00340 00341 template<class VT> 00342 int 00343 KMacroMapExpander<QChar,VT>::expandEscapedMacro( const QString &str, uint pos, QStringList &ret ) 00344 { 00345 if (str[pos + 1] == escapeChar()) { 00346 ret += QString( escapeChar() ); 00347 return 2; 00348 } 00349 00350 QMapConstIterator<QChar,VT> it = macromap.find(str[pos+1]); 00351 if (it != macromap.end()) { 00352 ret += it.data(); 00353 return 2; 00354 } 00355 return false; 00356 } 00357 00358 template<class VT> 00359 class KMacroMapExpander<QString,VT> : public KMacroExpanderBase { 00360 00361 public: 00362 KMacroMapExpander( const QMap<QString,VT> &map, QChar c = '%' ) : 00363 KMacroExpanderBase( c ), macromap( map ) {} 00364 00365 protected: 00366 virtual int expandPlainMacro( const QString &str, uint pos, QStringList &ret ); 00367 virtual int expandEscapedMacro( const QString &str, uint pos, QStringList &ret ); 00368 00369 private: 00370 bool isIdentifier(uint c) { return c == '_' || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9'); } 00371 QMap<QString,VT> macromap; 00372 }; 00373 00374 template<class VT> 00375 int 00376 KMacroMapExpander<QString,VT>::expandPlainMacro( const QString &str, uint pos, QStringList &ret ) 00377 { 00378 if (isIdentifier( str[pos - 1].unicode() )) 00379 return 0; 00380 uint sl; 00381 for (sl = 0; isIdentifier( str[pos + sl].unicode() ); sl++); 00382 if (!sl) 00383 return 0; 00384 QMapConstIterator<QString,VT> it = 00385 macromap.find( QConstString( str.unicode() + pos, sl ).string() ); 00386 if (it != macromap.end()) { 00387 ret += it.data(); 00388 return sl; 00389 } 00390 return false; 00391 } 00392 00393 template<class VT> 00394 int 00395 KMacroMapExpander<QString,VT>::expandEscapedMacro( const QString &str, uint pos, QStringList &ret ) 00396 { 00397 if (str[pos + 1] == escapeChar()) { 00398 ret += QString( escapeChar() ); 00399 return 2; 00400 } 00401 uint sl, rsl, rpos; 00402 if (str[pos + 1] == '{') { 00403 rpos = pos + 2; 00404 for (sl = 0; str[rpos + sl] != '}'; sl++) 00405 if (rpos + sl >= str.length()) 00406 return 0; 00407 rsl = sl + 3; 00408 } else { 00409 rpos = pos + 1; 00410 for (sl = 0; isIdentifier( str[rpos + sl].unicode() ); sl++); 00411 rsl = sl + 1; 00412 } 00413 if (!sl) 00414 return 0; 00415 QMapConstIterator<QString,VT> it = 00416 macromap.find( QConstString( str.unicode() + rpos, sl ).string() ); 00417 if (it != macromap.end()) { 00418 ret += it.data(); 00419 return rsl; 00420 } 00421 return false; 00422 } 00423 00425 00426 template<class KT,class VT> 00427 inline QString 00428 TexpandMacros( const QString &ostr, const QMap<KT,VT> &map, QChar c ) 00429 { 00430 QString str( ostr ); 00431 KMacroMapExpander<KT,VT> kmx( map, c ); 00432 kmx.expandMacros( str ); 00433 return str; 00434 } 00435 00436 template<class KT,class VT> 00437 inline QString 00438 TexpandMacrosShellQuote( const QString &ostr, const QMap<KT,VT> &map, QChar c ) 00439 { 00440 QString str( ostr ); 00441 KMacroMapExpander<KT,VT> kmx( map, c ); 00442 if (!kmx.expandMacrosShellQuote( str )) 00443 return QString::null; 00444 return str; 00445 } 00446 00447 // public API 00448 namespace KMacroExpander { 00449 00450 QString expandMacros( const QString &ostr, const QMap<QChar,QString> &map, QChar c ) { return TexpandMacros( ostr, map, c ); } 00451 QString expandMacrosShellQuote( const QString &ostr, const QMap<QChar,QString> &map, QChar c ) { return TexpandMacrosShellQuote( ostr, map, c ); } 00452 QString expandMacros( const QString &ostr, const QMap<QString,QString> &map, QChar c ) { return TexpandMacros( ostr, map, c ); } 00453 QString expandMacrosShellQuote( const QString &ostr, const QMap<QString,QString> &map, QChar c ) { return TexpandMacrosShellQuote( ostr, map, c ); } 00454 QString expandMacros( const QString &ostr, const QMap<QChar,QStringList> &map, QChar c ) { return TexpandMacros( ostr, map, c ); } 00455 QString expandMacrosShellQuote( const QString &ostr, const QMap<QChar,QStringList> &map, QChar c ) { return TexpandMacrosShellQuote( ostr, map, c ); } 00456 QString expandMacros( const QString &ostr, const QMap<QString,QStringList> &map, QChar c ) { return TexpandMacros( ostr, map, c ); } 00457 QString expandMacrosShellQuote( const QString &ostr, const QMap<QString,QStringList> &map, QChar c ) { return TexpandMacrosShellQuote( ostr, map, c ); } 00458 00459 } // namespace
KDE Logo
This file is part of the documentation for kdecore Library Version 3.2.3.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sun Oct 10 18:54:56 2004 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003