kdecore Library: kmacroexpander.cpp Source File (kdecore Library)

| # kdecore Library API Documentation
Overview | Class Hierarchy | Classes | Classes (annotated) | Members | Namespaces | Source Files

# 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

This file is part of the documentation for kdecore Library Version 3.4.1.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sat Jun 11 20:18:09 2005 by doxygen 1.4.1 written by Dimitri van Heesch, © 1997-2003