00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
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
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
00200 } else if (cc == '`') {
00201 str.replace( pos, 1, "$( " );
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
00251
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
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 }