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
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
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
00199 } else if (cc == '`') {
00200 str.replace( pos, 1, "$( " );
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
00250
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
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 }