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         rsl = sl + 3;
00406     } else {
00407         rpos = pos + 1;
00408         for (sl = 0; isIdentifier( str[rpos + sl].unicode() ); sl++);
00409         rsl = sl + 1;
00410     }
00411     if (!sl)
00412         return 0;
00413     QMapConstIterator<QString,VT> it =
00414         macromap.find( QConstString( str.unicode() + rpos, sl ).string() );
00415     if (it != macromap.end()) {
00416         ret += it.data();
00417         return rsl;
00418     }
00419     return false;
00420 }
00421 
00423 
00424 template<class KT,class VT>
00425 inline QString
00426 TexpandMacros( const QString &ostr, const QMap<KT,VT> &map, QChar c )
00427 {
00428     QString str( ostr );
00429     KMacroMapExpander<KT,VT> kmx( map, c );
00430     kmx.expandMacros( str );
00431     return str;
00432 }
00433 
00434 template<class KT,class VT>
00435 inline QString
00436 TexpandMacrosShellQuote( const QString &ostr, const QMap<KT,VT> &map, QChar c )
00437 {
00438     QString str( ostr );
00439     KMacroMapExpander<KT,VT> kmx( map, c );
00440     if (!kmx.expandMacrosShellQuote( str ))
00441         return QString::null;
00442     return str;
00443 }
00444 
00445 // public API
00446 namespace KMacroExpander {
00447 
00448   QString expandMacros( const QString &ostr, const QMap<QChar,QString> &map, QChar c ) { return TexpandMacros( ostr, map, c ); }
00449   QString expandMacrosShellQuote( const QString &ostr, const QMap<QChar,QString> &map, QChar c ) { return TexpandMacrosShellQuote( ostr, map, c ); }
00450   QString expandMacros( const QString &ostr, const QMap<QString,QString> &map, QChar c ) { return TexpandMacros( ostr, map, c ); }
00451   QString expandMacrosShellQuote( const QString &ostr, const QMap<QString,QString> &map, QChar c ) { return TexpandMacrosShellQuote( ostr, map, c ); }
00452   QString expandMacros( const QString &ostr, const QMap<QChar,QStringList> &map, QChar c ) { return TexpandMacros( ostr, map, c ); }
00453   QString expandMacrosShellQuote( const QString &ostr, const QMap<QChar,QStringList> &map, QChar c ) { return TexpandMacrosShellQuote( ostr, map, c ); }
00454   QString expandMacros( const QString &ostr, const QMap<QString,QStringList> &map, QChar c ) { return TexpandMacros( ostr, map, c ); }
00455   QString expandMacrosShellQuote( const QString &ostr, const QMap<QString,QStringList> &map, QChar c ) { return TexpandMacrosShellQuote( ostr, map, c ); }
00456 
00457 } // namespace
KDE Logo
This file is part of the documentation for kdelibs Version 3.1.4.
Documentation copyright © 1996-2002 the KDE developers.
Generated on Sun Feb 27 22:14:47 2005 by doxygen 1.3.4 written by Dimitri van Heesch, © 1997-2001