Kate
kateregexpsearch.cpp
Go to the documentation of this file.
00001 /* This file is part of the KDE libraries and the Kate part. 00002 * 00003 * Copyright (C) 2010 Bernhard Beschow <bbeschow@cs.tu-berlin.de> 00004 * Copyright (C) 2007 Sebastian Pipping <webmaster@hartwork.org> 00005 * 00006 * This library is free software; you can redistribute it and/or 00007 * modify it under the terms of the GNU Library General Public 00008 * License as published by the Free Software Foundation; either 00009 * version 2 of the License, or (at your option) any later version. 00010 * 00011 * This library is distributed in the hope that it will be useful, 00012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00014 * Library General Public License for more details. 00015 * 00016 * You should have received a copy of the GNU Library General Public License 00017 * along with this library; see the file COPYING.LIB. If not, write to 00018 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00019 * Boston, MA 02110-1301, USA. 00020 */ 00021 00022 //BEGIN includes 00023 #include "kateregexpsearch.h" 00024 #include "kateregexp.h" 00025 00026 #include <ktexteditor/document.h> 00027 //END includes 00028 00029 00030 00031 // Turn debug messages on/off here 00032 // #define FAST_DEBUG_ENABLE 00033 00034 #ifdef FAST_DEBUG_ENABLE 00035 # define FAST_DEBUG(x) kDebug( 13020 ) << x 00036 #else 00037 # define FAST_DEBUG(x) 00038 #endif 00039 00040 00041 class KateRegExpSearch::ReplacementStream 00042 { 00043 public: 00044 struct counter { 00045 counter(int value, int minWidth) 00046 : value(value) 00047 , minWidth(minWidth) 00048 {} 00049 00050 const int value; 00051 const int minWidth; 00052 }; 00053 00054 struct cap { 00055 cap(int n) 00056 : n(n) 00057 {} 00058 00059 const int n; 00060 }; 00061 00062 enum CaseConversion { 00063 upperCase, 00064 upperCaseFirst, 00065 lowerCase, 00066 lowerCaseFirst, 00067 keepCase 00068 }; 00069 00070 public: 00071 ReplacementStream(const QStringList &capturedTexts); 00072 00073 QString str() const { return m_str; } 00074 00075 ReplacementStream &operator<<(const QString &); 00076 ReplacementStream &operator<<(const counter &); 00077 ReplacementStream &operator<<(const cap &); 00078 ReplacementStream &operator<<(CaseConversion); 00079 00080 private: 00081 const QStringList m_capturedTexts; 00082 CaseConversion m_caseConversion; 00083 QString m_str; 00084 }; 00085 00086 00087 KateRegExpSearch::ReplacementStream::ReplacementStream(const QStringList &capturedTexts) 00088 : m_capturedTexts(capturedTexts) 00089 , m_caseConversion(keepCase) 00090 { 00091 } 00092 00093 KateRegExpSearch::ReplacementStream &KateRegExpSearch::ReplacementStream::operator<<(const QString &str) 00094 { 00095 switch (m_caseConversion) { 00096 case upperCase: 00097 // Copy as uppercase 00098 m_str.append(str.toUpper()); 00099 break; 00100 00101 case upperCaseFirst: 00102 if (str.length() > 0) { 00103 m_str.append(str.at(0).toUpper()); 00104 m_str.append(str.mid(1)); 00105 m_caseConversion = keepCase; 00106 } 00107 break; 00108 00109 case lowerCase: 00110 // Copy as lowercase 00111 m_str.append(str.toLower()); 00112 break; 00113 00114 case lowerCaseFirst: 00115 if (str.length() > 0) { 00116 m_str.append(str.at(0).toLower()); 00117 m_str.append(str.mid(1)); 00118 m_caseConversion = keepCase; 00119 } 00120 break; 00121 00122 case keepCase: // FALLTHROUGH 00123 default: 00124 // Copy unmodified 00125 m_str.append(str); 00126 break; 00127 00128 } 00129 00130 return *this; 00131 } 00132 00133 00134 KateRegExpSearch::ReplacementStream &KateRegExpSearch::ReplacementStream::operator<<(const counter &c) 00135 { 00136 // Zero padded counter value 00137 m_str.append(QString("%1").arg(c.value, c.minWidth, 10, QLatin1Char('0'))); 00138 00139 return *this; 00140 } 00141 00142 00143 KateRegExpSearch::ReplacementStream &KateRegExpSearch::ReplacementStream::operator<<(const cap &cap) 00144 { 00145 if (0 <= cap.n && cap.n < m_capturedTexts.size()) { 00146 (*this) << m_capturedTexts[cap.n]; 00147 } else { 00148 // Insert just the number to be consistent with QRegExp ("\c" becomes "c") 00149 m_str.append(QString::number(cap.n)); 00150 } 00151 00152 return *this; 00153 } 00154 00155 00156 KateRegExpSearch::ReplacementStream &KateRegExpSearch::ReplacementStream::operator<<(CaseConversion caseConversion) 00157 { 00158 m_caseConversion = caseConversion; 00159 00160 return *this; 00161 } 00162 00163 00164 //BEGIN d'tor, c'tor 00165 // 00166 // KateSearch Constructor 00167 // 00168 KateRegExpSearch::KateRegExpSearch ( KTextEditor::Document *document, Qt::CaseSensitivity caseSensitivity ) 00169 : m_document (document) 00170 , m_caseSensitivity (caseSensitivity) 00171 { 00172 } 00173 00174 // 00175 // KateSearch Destructor 00176 // 00177 KateRegExpSearch::~KateRegExpSearch() 00178 { 00179 } 00180 00181 00182 // helper structs for captures re-construction 00183 struct TwoViewCursor { 00184 int index; 00185 int openLine; 00186 int openCol; 00187 int closeLine; 00188 int closeCol; 00189 // note: open/close distinction does not seem needed 00190 // anymore. i keep it to make a potential way back 00191 // easier. overhead is minimal. 00192 }; 00193 00194 struct IndexPair { 00195 int openIndex; 00196 int closeIndex; 00197 }; 00198 00199 00200 00201 QVector<KTextEditor::Range> KateRegExpSearch::search( 00202 const QString &pattern, 00203 const KTextEditor::Range & inputRange, 00204 bool backwards) 00205 { 00206 // regex search 00207 KateRegExp regexp(pattern, m_caseSensitivity); 00208 00209 if (regexp.isEmpty() || !regexp.isValid() || !inputRange.isValid() || (inputRange.start() == inputRange.end())) 00210 { 00211 QVector<KTextEditor::Range> result; 00212 result.append(KTextEditor::Range::invalid()); 00213 return result; 00214 } 00215 00216 00217 // detect pattern type (single- or mutli-line) 00218 bool isMultiLine; 00219 00220 // detect '.' and '\s' and fix them 00221 const bool dotMatchesNewline = false; // TODO 00222 const int replacements = regexp.repairPattern(isMultiLine); 00223 if (dotMatchesNewline && (replacements > 0)) 00224 { 00225 isMultiLine = true; 00226 } 00227 00228 const int firstLineIndex = inputRange.start().line(); 00229 const int minColStart = inputRange.start().column(); 00230 // const int maxColEnd = inputRange.end().column(); 00231 if (isMultiLine) 00232 { 00233 // multi-line regex search (both forward and backward mode) 00234 QString wholeDocument; 00235 const int inputLineCount = inputRange.end().line() - inputRange.start().line() + 1; 00236 FAST_DEBUG("multi line search (lines " << firstLineIndex << ".." << firstLineIndex + inputLineCount - 1 << ")"); 00237 00238 // nothing to do... 00239 if (firstLineIndex >= m_document->lines()) 00240 { 00241 QVector<KTextEditor::Range> result; 00242 result.append(KTextEditor::Range::invalid()); 00243 return result; 00244 } 00245 00246 QVector<int> lineLens (inputLineCount); 00247 00248 // first line 00249 if (firstLineIndex < 0 || m_document->lines() <= firstLineIndex) 00250 { 00251 QVector<KTextEditor::Range> result; 00252 result.append(KTextEditor::Range::invalid()); 00253 return result; 00254 } 00255 00256 const QString firstLine = m_document->line(firstLineIndex); 00257 00258 const int firstLineLen = firstLine.length() - minColStart; 00259 wholeDocument.append(firstLine.right(firstLineLen)); 00260 lineLens[0] = firstLineLen; 00261 FAST_DEBUG(" line" << 0 << "has length" << lineLens[0]); 00262 00263 // second line and after 00264 const QString sep("\n"); 00265 for (int i = 1; i < inputLineCount; i++) 00266 { 00267 const int lineNum = firstLineIndex + i; 00268 if (lineNum < 0 || m_document->lines() <= lineNum) 00269 { 00270 QVector<KTextEditor::Range> result; 00271 result.append(KTextEditor::Range::invalid()); 00272 return result; 00273 } 00274 const QString text = m_document->line(lineNum); 00275 00276 lineLens[i] = text.length(); 00277 wholeDocument.append(sep); 00278 wholeDocument.append(text); 00279 FAST_DEBUG(" line" << i << "has length" << lineLens[i]); 00280 } 00281 00282 const int pos = backwards 00283 ? regexp.lastIndexIn(wholeDocument, 0, wholeDocument.length()) 00284 : regexp.indexIn(wholeDocument, 0, wholeDocument.length()); 00285 if (pos == -1) 00286 { 00287 // no match 00288 FAST_DEBUG("not found"); 00289 { 00290 QVector<KTextEditor::Range> result; 00291 result.append(KTextEditor::Range::invalid()); 00292 return result; 00293 } 00294 } 00295 00296 #ifdef FAST_DEBUG_ENABLE 00297 const int matchLen = regexp.matchedLength(); 00298 FAST_DEBUG("found at relative pos " << pos << ", length " << matchLen); 00299 #endif 00300 00301 // save opening and closing indices and build a map. 00302 // the correct values will be written into it later. 00303 QMap<int, TwoViewCursor *> indicesToCursors; 00304 const int numCaptures = regexp.numCaptures(); 00305 QVector<IndexPair> indexPairs(1 + numCaptures); 00306 for (int z = 0; z <= numCaptures; z++) 00307 { 00308 const int openIndex = regexp.pos(z); 00309 IndexPair & pair = indexPairs[z]; 00310 if (openIndex == -1) 00311 { 00312 // empty capture gives invalid 00313 pair.openIndex = -1; 00314 pair.closeIndex = -1; 00315 FAST_DEBUG("capture []"); 00316 } 00317 else 00318 { 00319 const int closeIndex = openIndex + regexp.cap(z).length(); 00320 pair.openIndex = openIndex; 00321 pair.closeIndex = closeIndex; 00322 FAST_DEBUG("capture [" << pair.openIndex << ".." << pair.closeIndex << "]"); 00323 00324 // each key no more than once 00325 if (!indicesToCursors.contains(openIndex)) 00326 { 00327 TwoViewCursor * twoViewCursor = new TwoViewCursor; 00328 twoViewCursor->index = openIndex; 00329 indicesToCursors.insert(openIndex, twoViewCursor); 00330 FAST_DEBUG(" border index added: " << openIndex); 00331 } 00332 if (!indicesToCursors.contains(closeIndex)) 00333 { 00334 TwoViewCursor * twoViewCursor = new TwoViewCursor; 00335 twoViewCursor->index = closeIndex; 00336 indicesToCursors.insert(closeIndex, twoViewCursor); 00337 FAST_DEBUG(" border index added: " << closeIndex); 00338 } 00339 } 00340 } 00341 00342 // find out where they belong 00343 int curRelLine = 0; 00344 int curRelCol = 0; 00345 int curRelIndex = 0; 00346 QMap<int, TwoViewCursor *>::const_iterator iter = indicesToCursors.constBegin(); 00347 while (iter != indicesToCursors.constEnd()) 00348 { 00349 // forward to index, save line/col 00350 const int index = (*iter)->index; 00351 FAST_DEBUG("resolving position" << index); 00352 TwoViewCursor & twoViewCursor = *(*iter); 00353 while (curRelIndex <= index) 00354 { 00355 FAST_DEBUG("walk pos (" << curRelLine << "," << curRelCol << ") = " 00356 << curRelIndex << "relative, steps more to go" << index - curRelIndex); 00357 const int curRelLineLen = lineLens[curRelLine]; 00358 const int curLineRemainder = curRelLineLen - curRelCol; 00359 const int lineFeedIndex = curRelIndex + curLineRemainder; 00360 if (index <= lineFeedIndex) { 00361 if (index == lineFeedIndex) { 00362 // on this line _on_ line feed 00363 FAST_DEBUG(" on line feed"); 00364 const int absLine = curRelLine + firstLineIndex; 00365 twoViewCursor.openLine 00366 = twoViewCursor.closeLine 00367 = absLine; 00368 twoViewCursor.openCol 00369 = twoViewCursor.closeCol 00370 = ((curRelLine == 0) ? minColStart : 0) + curRelLineLen; 00371 00372 // advance to next line 00373 const int advance = (index - curRelIndex) + 1; 00374 curRelLine++; 00375 curRelCol = 0; 00376 curRelIndex += advance; 00377 } else { // index < lineFeedIndex 00378 // on this line _before_ line feed 00379 FAST_DEBUG(" before line feed"); 00380 const int diff = (index - curRelIndex); 00381 const int absLine = curRelLine + firstLineIndex; 00382 const int absCol = ((curRelLine == 0) ? minColStart : 0) + curRelCol + diff; 00383 twoViewCursor.openLine 00384 = twoViewCursor.closeLine 00385 = absLine; 00386 twoViewCursor.openCol 00387 = twoViewCursor.closeCol 00388 = absCol; 00389 00390 // advance on same line 00391 const int advance = diff + 1; 00392 curRelCol += advance; 00393 curRelIndex += advance; 00394 } 00395 FAST_DEBUG("open(" << twoViewCursor.openLine << "," << twoViewCursor.openCol 00396 << ") close(" << twoViewCursor.closeLine << "," << twoViewCursor.closeCol << ")"); 00397 } 00398 else // if (index > lineFeedIndex) 00399 { 00400 // not on this line 00401 // advance to next line 00402 FAST_DEBUG(" not on this line"); 00403 const int advance = curLineRemainder + 1; 00404 curRelLine++; 00405 curRelCol = 0; 00406 curRelIndex += advance; 00407 } 00408 } 00409 00410 ++iter; 00411 } 00412 00413 // build result array 00414 QVector<KTextEditor::Range> result(1 + numCaptures); 00415 for (int y = 0; y <= numCaptures; y++) 00416 { 00417 IndexPair & pair = indexPairs[y]; 00418 if ((pair.openIndex == -1) || (pair.closeIndex == -1)) 00419 { 00420 result[y] = KTextEditor::Range::invalid(); 00421 } 00422 else 00423 { 00424 const TwoViewCursor * const openCursors = indicesToCursors[pair.openIndex]; 00425 const TwoViewCursor * const closeCursors = indicesToCursors[pair.closeIndex]; 00426 const int startLine = openCursors->openLine; 00427 const int startCol = openCursors->openCol; 00428 const int endLine = closeCursors->closeLine; 00429 const int endCol = closeCursors->closeCol; 00430 FAST_DEBUG("range " << y << ": (" << startLine << ", " << startCol << ")..(" << endLine << ", " << endCol << ")"); 00431 result[y] = KTextEditor::Range(startLine, startCol, endLine, endCol); 00432 } 00433 } 00434 00435 // free structs allocated for indicesToCursors 00436 iter = indicesToCursors.constBegin(); 00437 while (iter != indicesToCursors.constEnd()) 00438 { 00439 TwoViewCursor * const twoViewCursor = *iter; 00440 delete twoViewCursor; 00441 ++iter; 00442 } 00443 return result; 00444 } 00445 else 00446 { 00447 // single-line regex search (both forward of backward mode) 00448 const int minLeft = inputRange.start().column(); 00449 const uint maxRight = inputRange.end().column(); // first not included 00450 const int forMin = inputRange.start().line(); 00451 const int forMax = inputRange.end().line(); 00452 const int forInit = backwards ? forMax : forMin; 00453 const int forInc = backwards ? -1 : +1; 00454 FAST_DEBUG("single line " << (backwards ? forMax : forMin) << ".." 00455 << (backwards ? forMin : forMax)); 00456 for (int j = forInit; (forMin <= j) && (j <= forMax); j += forInc) 00457 { 00458 if (j < 0 || m_document->lines() <= j) 00459 { 00460 FAST_DEBUG("searchText | line " << j << ": no"); 00461 QVector<KTextEditor::Range> result; 00462 result.append(KTextEditor::Range::invalid()); 00463 return result; 00464 } 00465 const QString textLine = m_document->line(j); 00466 00467 // Find (and don't match ^ in between...) 00468 const int first = (j == forMin) ? minLeft : 0; 00469 const int last = (j == forMax) ? maxRight : textLine.length(); 00470 const int foundAt = (backwards ? regexp.lastIndexIn(textLine, first, last) 00471 : regexp.indexIn(textLine, first, last)); 00472 const bool found = (foundAt != -1); 00473 00474 /* 00475 TODO do we still need this? 00476 00477 // A special case which can only occur when searching with a regular expression consisting 00478 // only of a lookahead (e.g. ^(?=\{) for a function beginning without selecting '{'). 00479 if (myMatchLen == 0 && line == startPosition.line() && foundAt == (uint) col) 00480 { 00481 if (col < lineLength(line)) 00482 col++; 00483 else { 00484 line++; 00485 col = 0; 00486 } 00487 continue; 00488 } 00489 */ 00490 00491 if (found) 00492 { 00493 FAST_DEBUG("line " << j << ": yes"); 00494 00495 // build result array 00496 const int numCaptures = regexp.numCaptures(); 00497 QVector<KTextEditor::Range> result(1 + numCaptures); 00498 result[0] = KTextEditor::Range(j, foundAt, j, foundAt + regexp.matchedLength()); 00499 FAST_DEBUG("result range " << 0 << ": (" << j << ", " << foundAt << ")..(" << j << ", " << 00500 foundAt + regexp.matchedLength() << ")"); 00501 for (int y = 1; y <= numCaptures; y++) 00502 { 00503 const int openIndex = regexp.pos(y); 00504 if (openIndex == -1) 00505 { 00506 result[y] = KTextEditor::Range::invalid(); 00507 FAST_DEBUG("capture []"); 00508 } 00509 else 00510 { 00511 const int closeIndex = openIndex + regexp.cap(y).length(); 00512 FAST_DEBUG("result range " << y << ": (" << j << ", " << openIndex << ")..(" << j << ", " << closeIndex << ")"); 00513 result[y] = KTextEditor::Range(j, openIndex, j, closeIndex); 00514 } 00515 } 00516 return result; 00517 } 00518 else 00519 { 00520 FAST_DEBUG("searchText | line " << j << ": no"); 00521 } 00522 } 00523 } 00524 00525 QVector<KTextEditor::Range> result; 00526 result.append(KTextEditor::Range::invalid()); 00527 return result; 00528 } 00529 00530 00531 /*static*/ QString KateRegExpSearch::escapePlaintext(const QString & text) 00532 { 00533 return buildReplacement(text, QStringList(), 0, false); 00534 } 00535 00536 00537 /*static*/ QString KateRegExpSearch::buildReplacement(const QString & text, const QStringList &capturedTexts, int replacementCounter) 00538 { 00539 return buildReplacement(text, capturedTexts, replacementCounter, true); 00540 } 00541 00542 00543 /*static*/ QString KateRegExpSearch::buildReplacement(const QString & text, const QStringList &capturedTexts, int replacementCounter, bool replacementGoodies) { 00544 // get input 00545 const int inputLen = text.length(); 00546 int input = 0; // walker index 00547 00548 // prepare output 00549 ReplacementStream out(capturedTexts); 00550 00551 while (input < inputLen) 00552 { 00553 switch (text[input].unicode()) 00554 { 00555 case L'\n': 00556 out << text[input]; 00557 input++; 00558 break; 00559 00560 case L'\\': 00561 if (input + 1 >= inputLen) 00562 { 00563 // copy backslash 00564 out << text[input]; 00565 input++; 00566 break; 00567 } 00568 00569 switch (text[input + 1].unicode()) 00570 { 00571 case L'0': // "\0000".."\0377" 00572 if (input + 4 >= inputLen) 00573 { 00574 out << ReplacementStream::cap(0); 00575 input += 2; 00576 } 00577 else 00578 { 00579 bool stripAndSkip = false; 00580 const ushort text_2 = text[input + 2].unicode(); 00581 if ((text_2 >= L'0') && (text_2 <= L'3')) 00582 { 00583 const ushort text_3 = text[input + 3].unicode(); 00584 if ((text_3 >= L'0') && (text_3 <= L'7')) 00585 { 00586 const ushort text_4 = text[input + 4].unicode(); 00587 if ((text_4 >= L'0') && (text_4 <= L'7')) 00588 { 00589 int digits[3]; 00590 for (int i = 0; i < 3; i++) 00591 { 00592 digits[i] = 7 - (L'7' - text[input + 2 + i].unicode()); 00593 } 00594 const int ch = 64 * digits[0] + 8 * digits[1] + digits[2]; 00595 out << QChar(ch); 00596 input += 5; 00597 } 00598 else 00599 { 00600 stripAndSkip = true; 00601 } 00602 } 00603 else 00604 { 00605 stripAndSkip = true; 00606 } 00607 } 00608 else 00609 { 00610 stripAndSkip = true; 00611 } 00612 00613 if (stripAndSkip) 00614 { 00615 out << ReplacementStream::cap(0); 00616 input += 2; 00617 } 00618 } 00619 break; 00620 00621 case L'1': 00622 case L'2': 00623 case L'3': 00624 case L'4': 00625 case L'5': 00626 case L'6': 00627 case L'7': 00628 case L'8': 00629 case L'9': 00630 out << ReplacementStream::cap(9 - (L'9' - text[input + 1].unicode())); 00631 input += 2; 00632 break; 00633 00634 case L'E': // FALLTHROUGH 00635 case L'L': // FALLTHROUGH 00636 case L'l': // FALLTHROUGH 00637 case L'U': // FALLTHROUGH 00638 case L'u': 00639 if (!replacementGoodies) { 00640 // strip backslash ("\?" -> "?") 00641 out << text[input + 1]; 00642 } else { 00643 // handle case switcher 00644 switch (text[input + 1].unicode()) { 00645 case L'L': 00646 out << ReplacementStream::lowerCase; 00647 break; 00648 00649 case L'l': 00650 out << ReplacementStream::lowerCaseFirst; 00651 break; 00652 00653 case L'U': 00654 out << ReplacementStream::upperCase; 00655 break; 00656 00657 case L'u': 00658 out << ReplacementStream::upperCaseFirst; 00659 break; 00660 00661 case L'E': // FALLTHROUGH 00662 default: 00663 out << ReplacementStream::keepCase; 00664 00665 } 00666 } 00667 input += 2; 00668 break; 00669 00670 case L'#': 00671 if (!replacementGoodies) { 00672 // strip backslash ("\?" -> "?") 00673 out << text[input + 1]; 00674 input += 2; 00675 } else { 00676 // handle replacement counter 00677 // eat and count all following hash marks 00678 // each hash stands for a leading zero: \### will produces 001, 002, ... 00679 int minWidth = 1; 00680 while ((input + minWidth + 1 < inputLen) && (text[input + minWidth + 1].unicode() == L'#')) { 00681 minWidth++; 00682 } 00683 out << ReplacementStream::counter(replacementCounter, minWidth); 00684 input += 1 + minWidth; 00685 } 00686 break; 00687 00688 case L'a': 00689 out << QChar(0x07); 00690 input += 2; 00691 break; 00692 00693 case L'f': 00694 out << QChar(0x0c); 00695 input += 2; 00696 break; 00697 00698 case L'n': 00699 out << QChar(0x0a); 00700 input += 2; 00701 break; 00702 00703 case L'r': 00704 out << QChar(0x0d); 00705 input += 2; 00706 break; 00707 00708 case L't': 00709 out << QChar(0x09); 00710 input += 2; 00711 break; 00712 00713 case L'v': 00714 out << QChar(0x0b); 00715 input += 2; 00716 break; 00717 00718 case L'x': // "\x0000".."\xffff" 00719 if (input + 5 >= inputLen) 00720 { 00721 // strip backslash ("\x" -> "x") 00722 out << text[input + 1]; 00723 input += 2; 00724 } 00725 else 00726 { 00727 bool stripAndSkip = false; 00728 const ushort text_2 = text[input + 2].unicode(); 00729 if (((text_2 >= L'0') && (text_2 <= L'9')) 00730 || ((text_2 >= L'a') && (text_2 <= L'f')) 00731 || ((text_2 >= L'A') && (text_2 <= L'F'))) 00732 { 00733 const ushort text_3 = text[input + 3].unicode(); 00734 if (((text_3 >= L'0') && (text_3 <= L'9')) 00735 || ((text_3 >= L'a') && (text_3 <= L'f')) 00736 || ((text_3 >= L'A') && (text_3 <= L'F'))) 00737 { 00738 const ushort text_4 = text[input + 4].unicode(); 00739 if (((text_4 >= L'0') && (text_4 <= L'9')) 00740 || ((text_4 >= L'a') && (text_4 <= L'f')) 00741 || ((text_4 >= L'A') && (text_4 <= L'F'))) 00742 { 00743 const ushort text_5 = text[input + 5].unicode(); 00744 if (((text_5 >= L'0') && (text_5 <= L'9')) 00745 || ((text_5 >= L'a') && (text_5 <= L'f')) 00746 || ((text_5 >= L'A') && (text_5 <= L'F'))) 00747 { 00748 int digits[4]; 00749 for (int i = 0; i < 4; i++) 00750 { 00751 const ushort cur = text[input + 2 + i].unicode(); 00752 if ((cur >= L'0') && (cur <= L'9')) 00753 { 00754 digits[i] = 9 - (L'9' - cur); 00755 } 00756 else if ((cur >= L'a') && (cur <= L'f')) 00757 { 00758 digits[i] = 15 - (L'f' - cur); 00759 } 00760 else // if ((cur >= L'A') && (cur <= L'F'))) 00761 { 00762 digits[i] = 15 - (L'F' - cur); 00763 } 00764 } 00765 00766 const int ch = 4096 * digits[0] + 256 * digits[1] + 16 * digits[2] + digits[3]; 00767 out << QChar(ch); 00768 input += 6; 00769 } 00770 else 00771 { 00772 stripAndSkip = true; 00773 } 00774 } 00775 else 00776 { 00777 stripAndSkip = true; 00778 } 00779 } 00780 else 00781 { 00782 stripAndSkip = true; 00783 } 00784 } 00785 00786 if (stripAndSkip) 00787 { 00788 // strip backslash ("\x" -> "x") 00789 out << text[input + 1]; 00790 input += 2; 00791 } 00792 } 00793 break; 00794 00795 default: 00796 // strip backslash ("\?" -> "?") 00797 out << text[input + 1]; 00798 input += 2; 00799 00800 } 00801 break; 00802 00803 default: 00804 out << text[input]; 00805 input++; 00806 00807 } 00808 } 00809 00810 return out.str(); 00811 } 00812 00813 00814 // Kill our helpers again 00815 #ifdef FAST_DEBUG_ENABLE 00816 # undef FAST_DEBUG_ENABLE 00817 #endif 00818 #undef FAST_DEBUG 00819 00820 // kate: space-indent on; indent-width 2; replace-tabs on;
KDE 4.6 API Reference