1 | // |
2 | // Utility.cpp |
3 | // |
4 | // Library: CppParser |
5 | // Package: CppParser |
6 | // Module: Utility |
7 | // |
8 | // Copyright (c) 2006, Applied Informatics Software Engineering GmbH. |
9 | // and Contributors. |
10 | // |
11 | // SPDX-License-Identifier: BSL-1.0 |
12 | // |
13 | |
14 | |
15 | #include "Poco/CppParser/Utility.h" |
16 | #include "Poco/CppParser/Parser.h" |
17 | #include "Poco/CppParser/Struct.h" |
18 | #include "Poco/StringTokenizer.h" |
19 | #include "Poco/Glob.h" |
20 | #include "Poco/Path.h" |
21 | #include "Poco/File.h" |
22 | #include "Poco/Process.h" |
23 | #include "Poco/Environment.h" |
24 | #include "Poco/NumberFormatter.h" |
25 | #include "Poco/Exception.h" |
26 | #include <fstream> |
27 | #include <cstddef> |
28 | |
29 | |
30 | using Poco::StringTokenizer; |
31 | using Poco::Glob; |
32 | using Poco::Path; |
33 | using Poco::File; |
34 | using Poco::Process; |
35 | using Poco::ProcessHandle; |
36 | using Poco::Environment; |
37 | using Poco::NumberFormatter; |
38 | using Poco::Exception; |
39 | |
40 | |
41 | namespace Poco { |
42 | namespace CppParser { |
43 | |
44 | |
45 | void Utility::parseDir(const std::vector <std::string>& includePattern, const std::vector <std::string>& excludePattern, NameSpace::SymbolTable& st, const std::string& exec, const std::string& options, const std::string& path) |
46 | { |
47 | std::set<std::string> files; |
48 | Utility::buildFileList(files, includePattern, excludePattern); |
49 | for (std::set<std::string>::const_iterator it = files.begin(); it != files.end(); ++it) |
50 | { |
51 | Utility::parse(*it, st, exec, options, path); |
52 | } |
53 | Utility::fixup(st); |
54 | } |
55 | |
56 | |
57 | void Utility::parse(const std::string& file, NameSpace::SymbolTable& st, const std::string& exec, const std::string& options, const std::string& path) |
58 | { |
59 | std::string prepFile = Utility::preprocessFile(file, exec, options, path); |
60 | Utility::parseOnly(file, st, prepFile, true); |
61 | } |
62 | |
63 | |
64 | void Utility::fixup(NameSpace::SymbolTable& st) |
65 | { |
66 | for (NameSpace::SymbolTable::iterator it = st.begin(); it != st.end(); ++it) |
67 | { |
68 | Struct* pStruct = dynamic_cast<Struct*>(it->second); |
69 | if (pStruct) |
70 | { |
71 | pStruct->fixupBases(); |
72 | } |
73 | } |
74 | } |
75 | |
76 | |
77 | void Utility::detectPrefixAndIncludes(const std::string& origHFile, std::vector<std::string>& lines, std::string& prefix) |
78 | { |
79 | std::ifstream istr(origHFile.c_str()); |
80 | try |
81 | { |
82 | if (istr.good()) |
83 | { |
84 | std::string x; |
85 | istr >> x; |
86 | while (x.find("#ifndef" ) == std::string::npos) |
87 | istr >> x; |
88 | StringTokenizer tokenizer(x, " \t" , StringTokenizer::TOK_IGNORE_EMPTY | StringTokenizer::TOK_TRIM); |
89 | poco_assert (tokenizer.count() == 2); |
90 | StringTokenizer::Iterator itTmp = tokenizer.begin(); |
91 | ++itTmp; |
92 | std::string defValue = *itTmp; |
93 | istr >> x; |
94 | // now find the corresponding #define |
95 | while (x.find(defValue) == std::string::npos) |
96 | istr >> x; |
97 | //now parse until a class def is found without a ; at the end |
98 | bool stop = false; |
99 | std::string prefixHint; |
100 | do |
101 | { |
102 | istr >> x; |
103 | // we add EVERYTHING to lines: reason: used macros/preprocessor defines, conditional includes should all be present in the generated code |
104 | // just think about fwd declarations inside a NS_BEGIN ... NS_END block |
105 | if (x.find("class" ) != std::string::npos && x.find(";" ) == std::string::npos) |
106 | { |
107 | StringTokenizer tok(x, " \t" , StringTokenizer::TOK_IGNORE_EMPTY | StringTokenizer::TOK_TRIM); |
108 | StringTokenizer::Iterator it = tok.begin(); |
109 | while (*it != "class" && it != tok.end()) |
110 | ++it; |
111 | // the next after class must be *_API or in case of a template it must be the class name |
112 | ++it; |
113 | std::size_t apiPos = it->find("_API" ); |
114 | if (apiPos != std::string::npos) |
115 | { |
116 | prefixHint = it->substr(0, apiPos); |
117 | } |
118 | stop = true; |
119 | } |
120 | else |
121 | { |
122 | lines.push_back(x); |
123 | } |
124 | } |
125 | while (!stop && !istr.eof()); |
126 | if (!stop) |
127 | { |
128 | lines.clear(); |
129 | prefix.clear(); |
130 | //throw Poco::SyntaxException("Failed to parse file " + origHFile + ".No class declared?"); |
131 | return; |
132 | } |
133 | |
134 | // now search the prefix |
135 | if (lines.empty()) |
136 | { |
137 | prefix.clear(); |
138 | return; |
139 | } |
140 | // the prefix for that file |
141 | std::vector<std::string>::const_iterator it = lines.end(); |
142 | --it; |
143 | std::vector<std::string>::const_iterator itBegin = lines.begin(); |
144 | for (; it != itBegin; --it) |
145 | { |
146 | std::size_t prefixPos = it->find("_BEGIN" ); |
147 | if (prefixPos != std::string::npos) |
148 | { |
149 | prefix = it->substr(0, prefixPos); |
150 | if (prefix != prefixHint && !prefixHint.empty()) |
151 | { |
152 | throw Poco::SyntaxException("Conflicting prefixes detected: " + prefixHint + "<->" + prefix); |
153 | } |
154 | } |
155 | } |
156 | } |
157 | else throw Poco::OpenFileException(origHFile); |
158 | } |
159 | catch (Exception&) |
160 | { |
161 | istr.close(); |
162 | throw; |
163 | } |
164 | istr.close(); |
165 | } |
166 | |
167 | |
168 | std::string Utility::preprocessFile(const std::string& file, const std::string& exec, const std::string& options, const std::string& path) |
169 | { |
170 | Path pp(file); |
171 | pp.setExtension("i" ); |
172 | |
173 | std::string popts; |
174 | for (std::string::const_iterator it = options.begin(); it != options.end(); ++it) |
175 | { |
176 | if (*it == '%') |
177 | popts += pp.getBaseName(); |
178 | else |
179 | popts += *it; |
180 | } |
181 | StringTokenizer tokenizer(popts, ",;\n" , StringTokenizer::TOK_IGNORE_EMPTY | StringTokenizer::TOK_TRIM); |
182 | std::vector<std::string> args(tokenizer.begin(), tokenizer.end()); |
183 | #ifdef _WIN32 |
184 | std::string quotedFile("\"" ); |
185 | quotedFile += file; |
186 | quotedFile += "\"" ; |
187 | args.push_back(quotedFile); |
188 | #else |
189 | args.push_back(file); |
190 | #endif |
191 | if (!path.empty()) |
192 | { |
193 | std::string newPath(Environment::get("PATH" )); |
194 | newPath += Path::pathSeparator(); |
195 | newPath += path; |
196 | Environment::set("PATH" , path); |
197 | } |
198 | |
199 | ProcessHandle proc = Process::launch(exec, args); |
200 | int rc = Process::wait(proc); |
201 | if (rc != 0) |
202 | { |
203 | throw Poco::RuntimeException("Failed to process file" ); |
204 | } |
205 | |
206 | return pp.getFileName(); |
207 | } |
208 | |
209 | |
210 | void Utility::parseOnly(const std::string& file, NameSpace::SymbolTable& st, const std::string& preprocessedFile, bool removePreprocessedFile) |
211 | { |
212 | std::ifstream istr(preprocessedFile.c_str()); |
213 | try |
214 | { |
215 | if (istr.good()) |
216 | { |
217 | Parser parser(st, file, istr); |
218 | parser.parse(); |
219 | } |
220 | else throw Poco::OpenFileException(preprocessedFile); |
221 | } |
222 | catch (Exception&) |
223 | { |
224 | istr.close(); |
225 | if (removePreprocessedFile) |
226 | removeFile(preprocessedFile); |
227 | throw; |
228 | } |
229 | istr.close(); |
230 | if (removePreprocessedFile) |
231 | removeFile(preprocessedFile); |
232 | } |
233 | |
234 | |
235 | void Utility::removeFile(const std::string& preprocessedfile) |
236 | { |
237 | try |
238 | { |
239 | File f(preprocessedfile); |
240 | f.remove(); |
241 | } |
242 | catch (Exception&) |
243 | { |
244 | } |
245 | } |
246 | |
247 | |
248 | void Utility::buildFileList(std::set<std::string>& files, const std::vector<std::string>& includePattern, const std::vector<std::string>& excludePattern) |
249 | { |
250 | std::set<std::string> temp; |
251 | std::vector <std::string>::const_iterator itInc = includePattern.begin(); |
252 | std::vector <std::string>::const_iterator itIncEnd = includePattern.end(); |
253 | |
254 | int options(0); |
255 | #if defined(_WIN32) |
256 | options |= Glob::GLOB_CASELESS; |
257 | #endif |
258 | |
259 | for (; itInc != itIncEnd; ++itInc) |
260 | { |
261 | Glob::glob(*itInc, temp, options); |
262 | } |
263 | |
264 | for (std::set<std::string>::const_iterator it = temp.begin(); it != temp.end(); ++it) |
265 | { |
266 | Path p(*it); |
267 | bool include = true; |
268 | std::vector <std::string>::const_iterator itExc = excludePattern.begin(); |
269 | std::vector <std::string>::const_iterator itExcEnd = excludePattern.end(); |
270 | for (; itExc != itExcEnd; ++itExc) |
271 | { |
272 | Glob glob(*itExc, options); |
273 | if (glob.match(p.toString())) |
274 | include = false; |
275 | } |
276 | if (include) |
277 | files.insert(*it); |
278 | } |
279 | } |
280 | |
281 | |
282 | std::string replace(const std::string& input, const std::string& oldToken, const std::string& newToken) |
283 | { |
284 | std::string result; |
285 | std::size_t start = 0; |
286 | std::size_t pos = 0; |
287 | do |
288 | { |
289 | pos = input.find(oldToken, start); |
290 | result.append(input.substr(start, pos-start)); |
291 | if (pos != std::string::npos) |
292 | result.append(newToken); |
293 | start = pos + oldToken.length(); |
294 | } |
295 | while (pos != std::string::npos); |
296 | |
297 | return result; |
298 | } |
299 | |
300 | } } // namespace Poco::CppParser |
301 | |