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.3.1.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sat Jan 22 16:43:43 2005 by doxygen 1.3.9.1 written by Dimitri van Heesch, © 1997-2003