File indexing completed on 2024-05-19 15:45:09
0001 /* 0002 SPDX-FileCopyrightText: 2006 Matt Rogers <mattr@kde.org> 0003 SPDX-FileCopyrightText: 2008 Aleix Pol <aleixpol@gmail.com> 0004 0005 SPDX-License-Identifier: GPL-2.0-or-later 0006 */ 0007 0008 #include "cmakelistsparser.h" 0009 #include <debug.h> 0010 0011 #include <util/stack.h> 0012 #include <QDir> 0013 0014 QMap<QChar, QChar> whatToScape() 0015 { 0016 //Only add those where we're not scaping the next character 0017 QMap<QChar, QChar> ret{ 0018 {QLatin1Char('n'), QLatin1Char('\n')}, 0019 {QLatin1Char('r'), QLatin1Char('\r')}, 0020 {QLatin1Char('t'), QLatin1Char('\t')}, 0021 }; 0022 return ret; 0023 } 0024 0025 const QMap<QChar, QChar> CMakeFunctionArgument::scapings=whatToScape(); 0026 0027 constexpr QChar scapingChar = QLatin1Char('\\'); 0028 0029 QString CMakeFunctionArgument::unescapeValue(const QString& value) 0030 { 0031 int firstScape=value.indexOf(scapingChar); 0032 if (firstScape<0) 0033 { 0034 return value; 0035 } 0036 0037 QString newValue; 0038 int last=0; 0039 QMap<QChar, QChar>::const_iterator itEnd = scapings.constEnd(); 0040 for(int i=firstScape; i<value.size()-1 && i>=0; i=value.indexOf(scapingChar, i+2)) 0041 { 0042 newValue+=value.midRef(last, i-last); 0043 const QChar current=value[i+1]; 0044 QMap<QChar, QChar>::const_iterator it = scapings.constFind(current); 0045 0046 if(it!=itEnd) 0047 newValue += *it; 0048 else 0049 newValue += current; 0050 0051 last=i+2; 0052 } 0053 newValue+=value.midRef(last, value.size()); 0054 // qCDebug(CMAKE) << "escaping" << value << newValue; 0055 return newValue; 0056 } 0057 0058 void CMakeFunctionDesc::addArguments( const QStringList& args, bool addEvenIfEmpty ) 0059 { 0060 if(addEvenIfEmpty && args.isEmpty()) 0061 arguments += CMakeFunctionArgument(); 0062 else { 0063 arguments.reserve(arguments.size() + args.size()); 0064 for (const auto& arg : args) { 0065 CMakeFunctionArgument cmakeArg(arg); 0066 arguments.append(cmakeArg); 0067 } 0068 } 0069 } 0070 0071 QString CMakeFunctionDesc::writeBack() const 0072 { 0073 QStringList args; 0074 args.reserve(arguments.size()); 0075 for (const auto& arg : arguments) { 0076 if (arg.quoted) { 0077 args.append(QLatin1Char('"') + arg.value + QLatin1Char('"')); 0078 } else { 0079 args.append(arg.value); 0080 } 0081 } 0082 return name + QLatin1String("( ") + args.join(QLatin1Char(' ')) + QLatin1String(" )"); 0083 } 0084 0085 namespace CMakeListsParser 0086 { 0087 0088 static bool readCMakeFunction( cmListFileLexer* lexer, CMakeFunctionDesc& func); 0089 0090 CMakeFileContent readCMakeFile(const QString & _fileName) 0091 { 0092 cmListFileLexer* lexer = cmListFileLexer_New(); 0093 if ( !lexer ) 0094 return CMakeFileContent(); 0095 if ( !cmListFileLexer_SetFileName( lexer, qPrintable( _fileName ), nullptr ) ) { 0096 qCDebug(CMAKE) << "cmake read error. could not read " << _fileName; 0097 cmListFileLexer_Delete(lexer); 0098 return CMakeFileContent(); 0099 } 0100 0101 CMakeFileContent ret; 0102 QString fileName = QDir::cleanPath(_fileName); 0103 0104 bool readError = false, haveNewline = true; 0105 cmListFileLexer_Token* token; 0106 0107 while(!readError && (token = cmListFileLexer_Scan(lexer))) 0108 { 0109 readError=false; 0110 if(token->type == cmListFileLexer_Token_Newline) 0111 { 0112 readError=false; 0113 haveNewline = true; 0114 } 0115 else if(token->type == cmListFileLexer_Token_Identifier) 0116 { 0117 if(haveNewline) 0118 { 0119 haveNewline = false; 0120 CMakeFunctionDesc function; 0121 function.name = QString::fromLocal8Bit(token->text, token->length).toLower(); 0122 function.filePath = fileName; 0123 function.line = token->line; 0124 function.column = token->column; 0125 0126 readError = !readCMakeFunction( lexer, function); 0127 ret.append(function); 0128 0129 if(readError) 0130 { 0131 qCDebug(CMAKE) << "Error while parsing:" << function.name << "at" << function.line; 0132 } 0133 } 0134 } 0135 } 0136 cmListFileLexer_Delete(lexer); 0137 0138 return ret; 0139 } 0140 0141 } 0142 0143 bool CMakeListsParser::readCMakeFunction(cmListFileLexer *lexer, CMakeFunctionDesc &func) 0144 { 0145 // Command name has already been parsed. Read the left paren. 0146 cmListFileLexer_Token* token; 0147 if(!(token = cmListFileLexer_Scan(lexer))) 0148 { 0149 return false; 0150 } 0151 if(token->type != cmListFileLexer_Token_ParenLeft) 0152 { 0153 return false; 0154 } 0155 0156 // Arguments. 0157 int parenthesis=1; 0158 while((token = cmListFileLexer_Scan(lexer))) 0159 { 0160 switch(token->type) 0161 { 0162 case cmListFileLexer_Token_ParenRight: 0163 parenthesis--; 0164 if(parenthesis==0) { 0165 func.endLine=token->line; 0166 func.endColumn=token->column; 0167 return true; 0168 } else if(parenthesis<0) 0169 return false; 0170 else 0171 func.arguments << CMakeFunctionArgument( QString::fromLocal8Bit(token->text, token->length), false, token->line, token->column ); 0172 break; 0173 case cmListFileLexer_Token_ParenLeft: 0174 parenthesis++; 0175 func.arguments << CMakeFunctionArgument( QString::fromLocal8Bit(token->text, token->length), false, token->line, token->column ); 0176 break; 0177 case cmListFileLexer_Token_Identifier: 0178 case cmListFileLexer_Token_ArgumentBracket: 0179 case cmListFileLexer_Token_ArgumentUnquoted: 0180 func.arguments << CMakeFunctionArgument( QString::fromLocal8Bit(token->text, token->length), false, token->line, token->column ); 0181 break; 0182 case cmListFileLexer_Token_ArgumentQuoted: 0183 func.arguments << CMakeFunctionArgument( QString::fromLocal8Bit(token->text, token->length), true, token->line, token->column+1 ); 0184 break; 0185 case cmListFileLexer_Token_Space: 0186 case cmListFileLexer_Token_Newline: 0187 break; 0188 default: 0189 return false; 0190 } 0191 } 0192 0193 return false; 0194 } 0195 0196 CMakeFunctionDesc::CMakeFunctionDesc(const QString& name, const QStringList& args) 0197 : name(name) 0198 { 0199 addArguments(args); 0200 } 0201 0202 CMakeFunctionDesc::CMakeFunctionDesc() 0203 {} 0204 0205 bool CMakeFunctionDesc::operator==(const CMakeFunctionDesc & other) const 0206 { 0207 if(other.arguments.count()!=arguments.count() || name!=other.name) 0208 return false; 0209 0210 auto it=arguments.constBegin(); 0211 auto itOther=other.arguments.constBegin(); 0212 for(;it!=arguments.constEnd(); ++it, ++itOther) 0213 { 0214 if(*it!=*itOther) 0215 return false; 0216 } 0217 return true; 0218 } 0219 0220 CMakeFunctionArgument::CMakeFunctionArgument(const QString& v, bool q, quint32 l, quint32 c) 0221 : value(unescapeValue(v)), quoted(q), line(l), column(c) 0222 { 0223 } 0224 0225 CMakeFunctionArgument::CMakeFunctionArgument(const QString& v) 0226 : value(v) 0227 { 0228 }