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 static bool 00315 isIdentifier( uint c ) 00316 { 00317 return c == '_' || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9'); 00318 } 00319 00321 00322 template<class VT> 00323 class KMacroMapExpander<QChar,VT> : public KMacroExpanderBase { 00324 00325 public: 00326 KMacroMapExpander( const QMap<QChar,VT> &map, QChar c = '%' ) : 00327 KMacroExpanderBase( c ), macromap( map ) {} 00328 00329 protected: 00330 virtual int expandPlainMacro( const QString &str, uint pos, QStringList &ret ); 00331 virtual int expandEscapedMacro( const QString &str, uint pos, QStringList &ret ); 00332 00333 private: 00334 QMap<QChar,VT> macromap; 00335 }; 00336 00337 template<class VT> 00338 int 00339 KMacroMapExpander<QChar,VT>::expandPlainMacro( const QString &str, uint pos, QStringList &ret ) 00340 { 00341 QMapConstIterator<QChar,VT> it = macromap.find(str[pos]); 00342 if (it != macromap.end()) { 00343 ret += it.data(); 00344 return 1; 00345 } 00346 return 0; 00347 } 00348 00349 template<class VT> 00350 int 00351 KMacroMapExpander<QChar,VT>::expandEscapedMacro( const QString &str, uint pos, QStringList &ret ) 00352 { 00353 if (str[pos + 1] == escapeChar()) { 00354 ret += QString( escapeChar() ); 00355 return 2; 00356 } 00357 QMapConstIterator<QChar,VT> it = macromap.find(str[pos+1]); 00358 if (it != macromap.end()) { 00359 ret += it.data(); 00360 return 2; 00361 } 00362 00363 return 0; 00364 } 00365 00366 template<class VT> 00367 class KMacroMapExpander<QString,VT> : public KMacroExpanderBase { 00368 00369 public: 00370 KMacroMapExpander( const QMap<QString,VT> &map, QChar c = '%' ) : 00371 KMacroExpanderBase( c ), macromap( map ) {} 00372 00373 protected: 00374 virtual int expandPlainMacro( const QString &str, uint pos, QStringList &ret ); 00375 virtual int expandEscapedMacro( const QString &str, uint pos, QStringList &ret ); 00376 00377 private: 00378 QMap<QString,VT> macromap; 00379 }; 00380 00381 template<class VT> 00382 int 00383 KMacroMapExpander<QString,VT>::expandPlainMacro( const QString &str, uint pos, QStringList &ret ) 00384 { 00385 if (isIdentifier( str[pos - 1].unicode() )) 00386 return 0; 00387 uint sl; 00388 for (sl = 0; isIdentifier( str[pos + sl].unicode() ); sl++); 00389 if (!sl) 00390 return 0; 00391 QMapConstIterator<QString,VT> it = 00392 macromap.find( QConstString( str.unicode() + pos, sl ).string() ); 00393 if (it != macromap.end()) { 00394 ret += it.data(); 00395 return sl; 00396 } 00397 return 0; 00398 } 00399 00400 template<class VT> 00401 int 00402 KMacroMapExpander<QString,VT>::expandEscapedMacro( const QString &str, uint pos, QStringList &ret ) 00403 { 00404 if (str[pos + 1] == escapeChar()) { 00405 ret += QString( escapeChar() ); 00406 return 2; 00407 } 00408 uint sl, rsl, rpos; 00409 if (str[pos + 1] == '{') { 00410 rpos = pos + 2; 00411 for (sl = 0; str[rpos + sl] != '}'; sl++) 00412 if (rpos + sl >= str.length()) 00413 return 0; 00414 rsl = sl + 3; 00415 } else { 00416 rpos = pos + 1; 00417 for (sl = 0; isIdentifier( str[rpos + sl].unicode() ); sl++); 00418 rsl = sl + 1; 00419 } 00420 if (!sl) 00421 return 0; 00422 QMapConstIterator<QString,VT> it = 00423 macromap.find( QConstString( str.unicode() + rpos, sl ).string() ); 00424 if (it != macromap.end()) { 00425 ret += it.data(); 00426 return rsl; 00427 } 00428 return 0; 00429 } 00430 00432 00433 int 00434 KCharMacroExpander::expandPlainMacro( const QString &str, uint pos, QStringList &ret ) 00435 { 00436 if (expandMacro( str[pos], ret )) 00437 return 1; 00438 return 0; 00439 } 00440 00441 int 00442 KCharMacroExpander::expandEscapedMacro( const QString &str, uint pos, QStringList &ret ) 00443 { 00444 if (str[pos + 1] == escapeChar()) { 00445 ret += QString( escapeChar() ); 00446 return 2; 00447 } 00448 if (expandMacro( str[pos+1], ret )) 00449 return 2; 00450 return 0; 00451 } 00452 00453 int 00454 KWordMacroExpander::expandPlainMacro( const QString &str, uint pos, QStringList &ret ) 00455 { 00456 if (isIdentifier( str[pos - 1].unicode() )) 00457 return 0; 00458 uint sl; 00459 for (sl = 0; isIdentifier( str[pos + sl].unicode() ); sl++); 00460 if (!sl) 00461 return 0; 00462 if (expandMacro( QConstString( str.unicode() + pos, sl ).string(), ret )) 00463 return sl; 00464 return 0; 00465 } 00466 00467 int 00468 KWordMacroExpander::expandEscapedMacro( const QString &str, uint pos, QStringList &ret ) 00469 { 00470 if (str[pos + 1] == escapeChar()) { 00471 ret += QString( escapeChar() ); 00472 return 2; 00473 } 00474 uint sl, rsl, rpos; 00475 if (str[pos + 1] == '{') { 00476 rpos = pos + 2; 00477 for (sl = 0; str[rpos + sl] != '}'; sl++) 00478 if (rpos + sl >= str.length()) 00479 return 0; 00480 rsl = sl + 3; 00481 } else { 00482 rpos = pos + 1; 00483 for (sl = 0; isIdentifier( str[rpos + sl].unicode() ); sl++); 00484 rsl = sl + 1; 00485 } 00486 if (!sl) 00487 return 0; 00488 if (expandMacro( QConstString( str.unicode() + rpos, sl ).string(), ret )) 00489 return rsl; 00490 return 0; 00491 } 00492 00494 00495 template<class KT,class VT> 00496 inline QString 00497 TexpandMacros( const QString &ostr, const QMap<KT,VT> &map, QChar c ) 00498 { 00499 QString str( ostr ); 00500 KMacroMapExpander<KT,VT> kmx( map, c ); 00501 kmx.expandMacros( str ); 00502 return str; 00503 } 00504 00505 template<class KT,class VT> 00506 inline QString 00507 TexpandMacrosShellQuote( const QString &ostr, const QMap<KT,VT> &map, QChar c ) 00508 { 00509 QString str( ostr ); 00510 KMacroMapExpander<KT,VT> kmx( map, c ); 00511 if (!kmx.expandMacrosShellQuote( str )) 00512 return QString::null; 00513 return str; 00514 } 00515 00516 // public API 00517 namespace KMacroExpander { 00518 00519 QString expandMacros( const QString &ostr, const QMap<QChar,QString> &map, QChar c ) { return TexpandMacros( ostr, map, c ); } 00520 QString expandMacrosShellQuote( const QString &ostr, const QMap<QChar,QString> &map, QChar c ) { return TexpandMacrosShellQuote( ostr, map, c ); } 00521 QString expandMacros( const QString &ostr, const QMap<QString,QString> &map, QChar c ) { return TexpandMacros( ostr, map, c ); } 00522 QString expandMacrosShellQuote( const QString &ostr, const QMap<QString,QString> &map, QChar c ) { return TexpandMacrosShellQuote( ostr, map, c ); } 00523 QString expandMacros( const QString &ostr, const QMap<QChar,QStringList> &map, QChar c ) { return TexpandMacros( ostr, map, c ); } 00524 QString expandMacrosShellQuote( const QString &ostr, const QMap<QChar,QStringList> &map, QChar c ) { return TexpandMacrosShellQuote( ostr, map, c ); } 00525 QString expandMacros( const QString &ostr, const QMap<QString,QStringList> &map, QChar c ) { return TexpandMacros( ostr, map, c ); } 00526 QString expandMacrosShellQuote( const QString &ostr, const QMap<QString,QStringList> &map, QChar c ) { return TexpandMacrosShellQuote( ostr, map, c ); } 00527 00528 } // namespace
KDE Logo
This file is part of the documentation for kdecore Library Version 3.4.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Thu Apr 14 00:03:30 2005 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003