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
30using Poco::StringTokenizer;
31using Poco::Glob;
32using Poco::Path;
33using Poco::File;
34using Poco::Process;
35using Poco::ProcessHandle;
36using Poco::Environment;
37using Poco::NumberFormatter;
38using Poco::Exception;
39
40
41namespace Poco {
42namespace CppParser {
43
44
45void 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
57void 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
64void 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
77void 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
168std::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
210void 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
235void 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
248void 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
282std::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