File indexing completed on 2024-04-28 04:38:36

0001 /*
0002     SPDX-FileCopyrightText: 2007 David Nolden <david.nolden.kdevelop@art-master.de>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-only
0005 */
0006 
0007 #include "stringhelpers.h"
0008 
0009 #include "debuglog.h"
0010 
0011 #include <language/duchain/stringhelpers.h>
0012 
0013 #include <QChar>
0014 #include <QString>
0015 
0016 namespace {
0017 
0018 enum { T_ACCESS, T_PAREN, T_BRACKET, T_IDE, T_UNKNOWN, T_TEMP };
0019 
0020 }
0021 
0022 int Utils::expressionAt( const QString& text, int index ) {
0023 
0024   if( index == 0 )
0025     return 0;
0026 
0027   int last = T_UNKNOWN;
0028   int start = index;
0029   --index;
0030 
0031   while ( index > 0 ) {
0032     while ( index > 0 && text[ index ].isSpace() ) {
0033       --index;
0034     }
0035 
0036     QChar ch = text[ index ];
0037     const QStringRef ch2 = text.midRef(index - 1, 2);
0038     if ((last != T_IDE) && (ch.isLetterOrNumber() || ch == QLatin1Char('_'))) {
0039       while (index > 0 && (text[index].isLetterOrNumber() || text[index] == QLatin1Char('_'))) {
0040         --index;
0041       }
0042       last = T_IDE;
0043     } else if (last != T_IDE && ch == QLatin1Char(')')) {
0044       int count = 0;
0045       while ( index > 0 ) {
0046         QChar ch = text[ index ];
0047         if (ch == QLatin1Char('(')) {
0048           ++count;
0049         } else if (ch == QLatin1Char(')')) {
0050           --count;
0051         }
0052         --index;
0053         if ( count == 0 ) {
0054           //index;
0055           last = T_PAREN;
0056           break;
0057         }
0058       }
0059     } else if (last != T_IDE && ch == QLatin1Char('>') && ch2 != QLatin1String("->")) {
0060       int count = 0;
0061       while ( index > 0 ) {
0062         QChar ch = text[ index ];
0063         if (ch == QLatin1Char('<')) {
0064           ++count;
0065         } else if (ch == QLatin1Char('>')) {
0066           --count;
0067         } else if ( count == 0 ) {
0068           //--index;
0069           last = T_TEMP;
0070           break;
0071         }
0072         --index;
0073       }
0074     } else if (ch == QLatin1Char(']')) {
0075       int count = 0;
0076       while ( index > 0 ) {
0077         QChar ch = text[ index ];
0078         if (ch == QLatin1Char('[')) {
0079           ++count;
0080         } else if (ch == QLatin1Char(']')) {
0081           --count;
0082         } else if ( count == 0 ) {
0083           //--index;
0084           last = T_BRACKET;
0085           break;
0086         }
0087         --index;
0088       }
0089     } else if (ch == QLatin1Char('.')) {
0090       --index;
0091       last = T_ACCESS;
0092     } else if ( ch2 == QLatin1String("::") ) {
0093       index -= 2;
0094       last = T_ACCESS;
0095     } else if ( ch2 == QLatin1String("->") ) {
0096       index -= 2;
0097       last = T_ACCESS;
0098     } else {
0099       if ( start > index ) {
0100         ++index;
0101       }
0102       last = T_UNKNOWN;
0103       break;
0104     }
0105   }
0106 
0107   ///If we're at the first item, the above algorithm cannot be used safely,
0108   ///so just determine whether the sign is valid for the beginning of an expression, if it isn't reject it.
0109   if (index == 0 && start > index &&
0110       !(text[index].isLetterOrNumber() || text[index] == QLatin1Char('_') || text[index] == QLatin1Char(':'))) {
0111     ++index;
0112   }
0113 
0114   return index;
0115 }
0116 
0117 QString Utils::quoteExpression(const QString& expr)
0118 {
0119     return quote(expr, QLatin1Char('"'));
0120 }
0121 
0122 QString Utils::unquoteExpression(const QString& expr)
0123 {
0124     return unquote(expr, false);
0125 }
0126 
0127 QString Utils::quote(const QString& str, QChar quoteCh)
0128 {
0129     QString res = str;
0130     res.replace(QLatin1Char('\\'), QLatin1String("\\\\")).replace(quoteCh, QLatin1Char('\\') + quoteCh);
0131     return quoteCh + res + quoteCh;
0132 }
0133 
0134 QString Utils::unquote(const QString& str, bool unescapeUnicode, QChar quoteCh)
0135 {
0136     if (str.startsWith(quoteCh) && str.endsWith(quoteCh)) {
0137         QString res;
0138         res.reserve(str.length());
0139         bool esc = false;
0140         int type = 0;
0141         QString escSeq;
0142         escSeq.reserve(4);
0143         // skip beginning and ending quoteCh, no need for str = str.mid(1, str.length() - 2)
0144         for (int i = 1; i != str.length() - 1; i++) {
0145             auto ch = str[i];
0146             if (esc) {
0147                 switch (ch.unicode()) {
0148                 case '\\':
0149                     if (type != 0) {
0150                         escSeq += ch;
0151                         qCDebug(DEBUGGERCOMMON) << "Unrecognized escape sequence:" << escSeq;
0152                         res += QLatin1Char('\\');
0153                         res += escSeq;
0154                         escSeq.clear();
0155                         esc = false;
0156                         type = 0;
0157                     } else {
0158                         res.append(QLatin1Char('\\'));
0159                         // escSeq.clear();    // escSeq must be empty.
0160                         esc = false;
0161                     }
0162                     break;
0163                 case 'u':
0164                 case 'x':
0165                     if (type != 0 || !unescapeUnicode) {
0166                         escSeq += ch;
0167                         qCDebug(DEBUGGERCOMMON) << "Unrecognized escape sequence:" << escSeq;
0168                         res += QLatin1Char('\\');
0169                         res += escSeq;
0170                         escSeq.clear();
0171                         esc = false;
0172                         type = 0;
0173                     } else {
0174                         type = ch == QLatin1Char('u') ? 1 : 2;
0175                     }
0176                     break;
0177                 case '0': case '1': case '2': case '3': case '4':
0178                 case '5': case '6': case '7': case '8': case '9':
0179                 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
0180                 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
0181                     escSeq += ch;
0182                     if (type == 0) {
0183                         qCDebug(DEBUGGERCOMMON) << "Unrecognized escape sequence:" << escSeq;
0184                         res += QLatin1Char('\\');
0185                         res += escSeq;
0186                         escSeq.clear();
0187                         esc = false;
0188                         type = 0;
0189                     } else {
0190                         // \uNNNN
0191                         // \xNN
0192                         if (escSeq.length() == (type == 1 ? 4 : 2)) {
0193                             // no need to handle error, we know for sure escSeq is '[0-9a-fA-F]+'
0194                             auto code = escSeq.toInt(nullptr, 16);
0195                             res += QChar(code);
0196                             escSeq.clear();
0197                             esc = false;
0198                             type = 0;
0199                         }
0200                     }
0201                     break;
0202                 default:
0203                     if (type == 0 && ch == quoteCh) {
0204                         res += ch;
0205                     } else {
0206                         escSeq += ch;
0207                         qCDebug(DEBUGGERCOMMON) << "Unrecognized escape sequence:" << escSeq;
0208                         res += QLatin1Char('\\');
0209                         res += escSeq;
0210                         escSeq.clear();
0211                     }
0212                     esc = false;
0213                     type = 0;
0214                     break;
0215                 }
0216             } else {
0217                 if (ch == QLatin1Char('\\')) {
0218                     esc = true;
0219                     continue;
0220                 }
0221                 res += ch;
0222             }
0223         }
0224         return res;
0225     } else {
0226         return str;
0227     }
0228 }