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 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
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 }