• Skip to content
  • Skip to link menu
KDE 3.5 API Reference
  • KDE API Reference
  • API Reference
  • Sitemap
  • Contact Us
 

KDECore

kmacroexpander.cpp

Go to the documentation of this file.
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., 51 Franklin Street, Fifth Floor,
00020     Boston, MA 02110-1301, 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 
00085     enum Quoting { noquote, singlequote, doublequote, dollarquote, 
00086                    paren, subst, group, math };
00087     typedef struct {
00088         Quoting current;
00089         bool dquote;
00090     } State;
00091     typedef struct {
00092         QString str;
00093         uint pos;
00094     } Save;
00095 
00096 }
00097 
00098 using namespace KMacroExpander;
00099 
00100 bool KMacroExpanderBase::expandMacrosShellQuote( QString &str, uint &pos )
00101 {
00102     int len;
00103     uint pos2;
00104     QChar ec( escapechar );
00105     State state = { noquote, false };
00106     QValueStack<State> sstack;
00107     QValueStack<Save> ostack;
00108     QStringList rst;
00109     QString rsts;
00110 
00111     while (pos < str.length()) {
00112         QChar cc( str.unicode()[pos] );
00113         if (ec != (char)0) {
00114             if (cc != ec)
00115                 goto nohit;
00116             if (!(len = expandEscapedMacro( str, pos, rst )))
00117                 goto nohit;
00118         } else {
00119             if (!(len = expandPlainMacro( str, pos, rst )))
00120                 goto nohit;
00121         }
00122             if (len < 0) {
00123                 pos -= len;
00124                 continue;
00125             }
00126             if (state.dquote) {
00127                 rsts = rst.join( " " );
00128                 rsts.replace( QRegExp("([$`\"\\\\])"), "\\\\1" );
00129             } else if (state.current == dollarquote) {
00130                 rsts = rst.join( " " );
00131                 rsts.replace( QRegExp("(['\\\\])"), "\\\\1" );
00132             } else if (state.current == singlequote) {
00133                 rsts = rst.join( " " );
00134                 rsts.replace( '\'', "'\\''");
00135             } else {
00136                 if (rst.isEmpty()) {
00137                     str.remove( pos, len );
00138                     continue;
00139                 } else {
00140                     rsts = "'";
00141 #if 0 // this could pay off if join() would be cleverer and the strings were long
00142                     for (QStringList::Iterator it = rst.begin(); it != rst.end(); ++it)
00143                         (*it).replace( '\'', "'\\''" );
00144                     rsts += rst.join( "' '" );
00145 #else
00146                     for (QStringList::ConstIterator it = rst.begin(); it != rst.end(); ++it) {
00147                         if (it != rst.begin())
00148                             rsts += "' '";
00149                         QString trsts( *it );
00150                         trsts.replace( '\'', "'\\''" );
00151                         rsts += trsts;
00152                     }
00153 #endif
00154                     rsts += "'";
00155                 }
00156             }
00157             rst.clear();
00158             str.replace( pos, len, rsts );
00159             pos += rsts.length();
00160             continue;
00161       nohit:
00162         if (state.current == singlequote) {
00163             if (cc == '\'')
00164                 state = sstack.pop();
00165         } else if (cc == '\\') {
00166             // always swallow the char -> prevent anomalies due to expansion
00167             pos += 2;
00168             continue;
00169         } else if (state.current == dollarquote) {
00170             if (cc == '\'')
00171                 state = sstack.pop();
00172         } else if (cc == '$') {
00173             cc = str[++pos];
00174             if (cc == '(') {
00175                 sstack.push( state );
00176                 if (str[pos + 1] == '(') {
00177                     Save sav = { str, pos + 2 };
00178                     ostack.push( sav );
00179                     state.current = math;
00180                     pos += 2;
00181                     continue;
00182                 } else {
00183                     state.current = paren;
00184                     state.dquote = false;
00185                 }
00186             } else if (cc == '{') {
00187                 sstack.push( state );
00188                 state.current = subst;
00189             } else if (!state.dquote) {
00190                 if (cc == '\'') {
00191                     sstack.push( state );
00192                     state.current = dollarquote;
00193                 } else if (cc == '"') {
00194                     sstack.push( state );
00195                     state.current = doublequote;
00196                     state.dquote = true;
00197                 }
00198             }
00199             // always swallow the char -> prevent anomalies due to expansion
00200         } else if (cc == '`') {
00201             str.replace( pos, 1, "$( " ); // add space -> avoid creating $((
00202             pos2 = pos += 3;
00203             for (;;) {
00204                 if (pos2 >= str.length()) {
00205                     pos = pos2;
00206                     return false;
00207                 }
00208                 cc = str.unicode()[pos2];
00209                 if (cc == '`')
00210                     break;
00211                 if (cc == '\\') {
00212                     cc = str[++pos2];
00213                     if (cc == '$' || cc == '`' || cc == '\\' ||
00214                         (cc == '"' && state.dquote))
00215                     {
00216                         str.remove( pos2 - 1, 1 );
00217                         continue;
00218                     }
00219                 }
00220                 pos2++;
00221             }
00222             str[pos2] = ')';
00223             sstack.push( state );
00224             state.current = paren;
00225             state.dquote = false;
00226             continue;
00227         } else if (state.current == doublequote) {
00228             if (cc == '"')
00229                 state = sstack.pop();
00230         } else if (cc == '\'') {
00231             if (!state.dquote) {
00232                 sstack.push( state );
00233                 state.current = singlequote;
00234             }
00235         } else if (cc == '"') {
00236             if (!state.dquote) {
00237                 sstack.push( state );
00238                 state.current = doublequote;
00239                 state.dquote = true;
00240             }
00241         } else if (state.current == subst) {
00242             if (cc == '}')
00243                 state = sstack.pop();
00244         } else if (cc == ')') {
00245             if (state.current == math) {
00246                 if (str[pos + 1] == ')') {
00247                     state = sstack.pop();
00248                     pos += 2;
00249                 } else {
00250                     // false hit: the $(( was a $( ( in fact
00251                     // ash does not care, but bash does
00252                     pos = ostack.top().pos;
00253                     str = ostack.top().str;
00254                     ostack.pop();
00255                     state.current = paren;
00256                     state.dquote = false;
00257                     sstack.push( state );
00258                 }
00259                 continue;
00260             } else if (state.current == paren)
00261                 state = sstack.pop();
00262             else
00263                 break;
00264         } else if (cc == '}') {
00265             if (state.current == KMacroExpander::group)
00266                 state = sstack.pop();
00267             else
00268                 break;
00269         } else if (cc == '(') {
00270             sstack.push( state );
00271             state.current = paren;
00272         } else if (cc == '{') {
00273             sstack.push( state );
00274             state.current = KMacroExpander::group;
00275         }
00276         pos++;
00277     }
00278     return sstack.empty();
00279 }
00280 
00281 bool KMacroExpanderBase::expandMacrosShellQuote( QString &str )
00282 {
00283   uint pos = 0;
00284   return expandMacrosShellQuote( str, pos ) && pos == str.length();
00285 }
00286 
00287 int KMacroExpanderBase::expandPlainMacro( const QString &, uint, QStringList & )
00288 { qFatal( "KMacroExpanderBase::expandPlainMacro called!" ); return 0; }
00289 
00290 int KMacroExpanderBase::expandEscapedMacro( const QString &, uint, QStringList & )
00291 { qFatal( "KMacroExpanderBase::expandEscapedMacro called!" ); return 0; }
00292 
00293 
00295 
00296 template<class KT,class VT>
00297 class KMacroMapExpander : public KMacroExpanderBase {
00298 
00299 public:
00300     KMacroMapExpander( const QMap<KT,VT> &map, QChar c = '%' ) :
00301         KMacroExpanderBase( c ), macromap( map ) {}
00302 
00303 protected:
00304     virtual int expandPlainMacro( const QString &str, uint pos, QStringList &ret );
00305     virtual int expandEscapedMacro( const QString &str, uint pos, QStringList &ret );
00306 
00307 private:
00308     QMap<KT,VT> macromap;
00309 };
00310 
00311 static QStringList &operator+=( QStringList &s, const QString &n) { s << n; return s; }
00312 
00314 
00315 static bool
00316 isIdentifier( uint c )
00317 {
00318     return c == '_' || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9');
00319 }
00320 
00322 
00323 template<class VT>
00324 class KMacroMapExpander<QChar,VT> : public KMacroExpanderBase {
00325 
00326 public:
00327     KMacroMapExpander( const QMap<QChar,VT> &map, QChar c = '%' ) :
00328         KMacroExpanderBase( c ), macromap( map ) {}
00329 
00330 protected:
00331     virtual int expandPlainMacro( const QString &str, uint pos, QStringList &ret );
00332     virtual int expandEscapedMacro( const QString &str, uint pos, QStringList &ret );
00333 
00334 private:
00335     QMap<QChar,VT> macromap;
00336 };
00337 
00338 template<class VT>
00339 int
00340 KMacroMapExpander<QChar,VT>::expandPlainMacro( const QString &str, uint pos, QStringList &ret )
00341 {
00342     QMapConstIterator<QChar,VT> it = macromap.find(str[pos]);
00343     if (it != macromap.end()) {
00344        ret += it.data();
00345        return 1;
00346     }
00347     return 0;
00348 }
00349 
00350 template<class VT>
00351 int
00352 KMacroMapExpander<QChar,VT>::expandEscapedMacro( const QString &str, uint pos, QStringList &ret )
00353 {
00354     if (str[pos + 1] == escapeChar()) {
00355         ret += QString( escapeChar() );
00356         return 2;
00357     }
00358     QMapConstIterator<QChar,VT> it = macromap.find(str[pos+1]);
00359     if (it != macromap.end()) {
00360        ret += it.data();
00361        return 2;
00362     }
00363 
00364     return 0;
00365 }
00366 
00367 template<class VT>
00368 class KMacroMapExpander<QString,VT> : public KMacroExpanderBase {
00369 
00370 public:
00371     KMacroMapExpander( const QMap<QString,VT> &map, QChar c = '%' ) :
00372         KMacroExpanderBase( c ), macromap( map ) {}
00373 
00374 protected:
00375     virtual int expandPlainMacro( const QString &str, uint pos, QStringList &ret );
00376     virtual int expandEscapedMacro( const QString &str, uint pos, QStringList &ret );
00377 
00378 private:
00379     QMap<QString,VT> macromap;
00380 };
00381 
00382 template<class VT>
00383 int
00384 KMacroMapExpander<QString,VT>::expandPlainMacro( const QString &str, uint pos, QStringList &ret )
00385 {
00386     if (isIdentifier( str[pos - 1].unicode() ))
00387         return 0;
00388     uint sl;
00389     for (sl = 0; isIdentifier( str[pos + sl].unicode() ); sl++);
00390     if (!sl)
00391         return 0;
00392     QMapConstIterator<QString,VT> it =
00393         macromap.find( QConstString( str.unicode() + pos, sl ).string() );
00394     if (it != macromap.end()) {
00395         ret += it.data();
00396         return sl;
00397     }
00398     return 0;
00399 }
00400 
00401 template<class VT>
00402 int
00403 KMacroMapExpander<QString,VT>::expandEscapedMacro( const QString &str, uint pos, QStringList &ret )
00404 {
00405     if (str[pos + 1] == escapeChar()) {
00406         ret += QString( escapeChar() );
00407         return 2;
00408     }
00409     uint sl, rsl, rpos;
00410     if (str[pos + 1] == '{') {
00411         rpos = pos + 2;
00412         for (sl = 0; str[rpos + sl] != '}'; sl++)
00413             if (rpos + sl >= str.length())
00414                 return 0;
00415         rsl = sl + 3;
00416     } else {
00417         rpos = pos + 1;
00418         for (sl = 0; isIdentifier( str[rpos + sl].unicode() ); sl++);
00419         rsl = sl + 1;
00420     }
00421     if (!sl)
00422         return 0;
00423     QMapConstIterator<QString,VT> it =
00424         macromap.find( QConstString( str.unicode() + rpos, sl ).string() );
00425     if (it != macromap.end()) {
00426         ret += it.data();
00427         return rsl;
00428     }
00429     return 0;
00430 }
00431 
00433 
00434 int
00435 KCharMacroExpander::expandPlainMacro( const QString &str, uint pos, QStringList &ret )
00436 {
00437     if (expandMacro( str[pos], ret ))
00438         return 1;
00439     return 0;
00440 }
00441 
00442 int
00443 KCharMacroExpander::expandEscapedMacro( const QString &str, uint pos, QStringList &ret )
00444 {
00445     if (str[pos + 1] == escapeChar()) {
00446         ret += QString( escapeChar() );
00447         return 2;
00448     }
00449     if (expandMacro( str[pos+1], ret ))
00450         return 2;
00451     return 0;
00452 }
00453 
00454 int
00455 KWordMacroExpander::expandPlainMacro( const QString &str, uint pos, QStringList &ret )
00456 {
00457     if (isIdentifier( str[pos - 1].unicode() ))
00458         return 0;
00459     uint sl;
00460     for (sl = 0; isIdentifier( str[pos + sl].unicode() ); sl++);
00461     if (!sl)
00462         return 0;
00463     if (expandMacro( QConstString( str.unicode() + pos, sl ).string(), ret ))
00464         return sl;
00465     return 0;
00466 }
00467 
00468 int
00469 KWordMacroExpander::expandEscapedMacro( const QString &str, uint pos, QStringList &ret )
00470 {
00471     if (str[pos + 1] == escapeChar()) {
00472         ret += QString( escapeChar() );
00473         return 2;
00474     }
00475     uint sl, rsl, rpos;
00476     if (str[pos + 1] == '{') {
00477         rpos = pos + 2;
00478         for (sl = 0; str[rpos + sl] != '}'; sl++)
00479             if (rpos + sl >= str.length())
00480                 return 0;
00481         rsl = sl + 3;
00482     } else {
00483         rpos = pos + 1;
00484         for (sl = 0; isIdentifier( str[rpos + sl].unicode() ); sl++);
00485         rsl = sl + 1;
00486     }
00487     if (!sl)
00488         return 0;
00489     if (expandMacro( QConstString( str.unicode() + rpos, sl ).string(), ret ))
00490         return rsl;
00491     return 0;
00492 }
00493 
00495 
00496 template<class KT,class VT>
00497 inline QString
00498 TexpandMacros( const QString &ostr, const QMap<KT,VT> &map, QChar c )
00499 {
00500     QString str( ostr );
00501     KMacroMapExpander<KT,VT> kmx( map, c );
00502     kmx.expandMacros( str );
00503     return str;
00504 }
00505 
00506 template<class KT,class VT>
00507 inline QString
00508 TexpandMacrosShellQuote( const QString &ostr, const QMap<KT,VT> &map, QChar c )
00509 {
00510     QString str( ostr );
00511     KMacroMapExpander<KT,VT> kmx( map, c );
00512     if (!kmx.expandMacrosShellQuote( str ))
00513         return QString::null;
00514     return str;
00515 }
00516 
00517 // public API
00518 namespace KMacroExpander {
00519 
00520   QString expandMacros( const QString &ostr, const QMap<QChar,QString> &map, QChar c ) { return TexpandMacros( ostr, map, c ); }
00521   QString expandMacrosShellQuote( const QString &ostr, const QMap<QChar,QString> &map, QChar c ) { return TexpandMacrosShellQuote( ostr, map, c ); }
00522   QString expandMacros( const QString &ostr, const QMap<QString,QString> &map, QChar c ) { return TexpandMacros( ostr, map, c ); }
00523   QString expandMacrosShellQuote( const QString &ostr, const QMap<QString,QString> &map, QChar c ) { return TexpandMacrosShellQuote( ostr, map, c ); }
00524   QString expandMacros( const QString &ostr, const QMap<QChar,QStringList> &map, QChar c ) { return TexpandMacros( ostr, map, c ); }
00525   QString expandMacrosShellQuote( const QString &ostr, const QMap<QChar,QStringList> &map, QChar c ) { return TexpandMacrosShellQuote( ostr, map, c ); }
00526   QString expandMacros( const QString &ostr, const QMap<QString,QStringList> &map, QChar c ) { return TexpandMacros( ostr, map, c ); }
00527   QString expandMacrosShellQuote( const QString &ostr, const QMap<QString,QStringList> &map, QChar c ) { return TexpandMacrosShellQuote( ostr, map, c ); }
00528 
00529 } // namespace

KDECore

Skip menu "KDECore"
  • Main Page
  • Modules
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

API Reference

Skip menu "API Reference"
  • dcop
  • DNSSD
  • interfaces
  • Kate
  • kconf_update
  • KDECore
  • KDED
  • kdefx
  • KDEsu
  • kdeui
  • KDocTools
  • KHTML
  • KImgIO
  • KInit
  • kio
  • kioslave
  • KJS
  • KNewStuff
  • KParts
  • KUtils
Generated for API Reference by doxygen 1.5.9
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal