1 | #include "utils.h" |
2 | #include "systemconsts.h" |
3 | #include <QDate> |
4 | #include <QDateTime> |
5 | #include <QApplication> |
6 | #include "editor.h" |
7 | #include "editorlist.h" |
8 | #include "settings.h" |
9 | #include "mainwindow.h" |
10 | #include "project.h" |
11 | #include "parser/cppparser.h" |
12 | #include "compiler/executablerunner.h" |
13 | #include <QMimeDatabase> |
14 | #ifdef Q_OS_WIN |
15 | #include <windows.h> |
16 | #endif |
17 | |
18 | QStringList splitProcessCommand(const QString &cmd) |
19 | { |
20 | QStringList result; |
21 | SplitProcessCommandQuoteType quoteType = SplitProcessCommandQuoteType::None; |
22 | int i=0; |
23 | QString current; |
24 | while (i<cmd.length()) { |
25 | switch (cmd[i].unicode()) { |
26 | case ' ': |
27 | case '\t': |
28 | case '\r': |
29 | case '\n': |
30 | if (quoteType == SplitProcessCommandQuoteType::None) { |
31 | if (!current.isEmpty()) { |
32 | result.append(current); |
33 | } |
34 | current = "" ; |
35 | } else { |
36 | current += cmd[i]; |
37 | } |
38 | i++; |
39 | break; |
40 | case '\"': |
41 | switch(quoteType) { |
42 | case SplitProcessCommandQuoteType::None: |
43 | quoteType = SplitProcessCommandQuoteType::Double; |
44 | break; |
45 | case SplitProcessCommandQuoteType::Double: |
46 | quoteType = SplitProcessCommandQuoteType::None; |
47 | break; |
48 | default: |
49 | current+=cmd[i]; |
50 | } |
51 | i++; |
52 | break; |
53 | case '\'': |
54 | switch(quoteType) { |
55 | case SplitProcessCommandQuoteType::None: |
56 | quoteType = SplitProcessCommandQuoteType::Single; |
57 | break; |
58 | case SplitProcessCommandQuoteType::Single: |
59 | quoteType = SplitProcessCommandQuoteType::None; |
60 | break; |
61 | default: |
62 | current+=cmd[i]; |
63 | } |
64 | i++; |
65 | break; |
66 | case '\\': |
67 | current += cmd[i]; |
68 | i++; |
69 | if (i<cmd.length()) { |
70 | current += cmd[i]; |
71 | i++; |
72 | } |
73 | break; |
74 | default: |
75 | current += cmd[i]; |
76 | i++; |
77 | } |
78 | } |
79 | if (!current.isEmpty()) |
80 | result.append(current); |
81 | return result; |
82 | } |
83 | |
84 | FileType getFileType(const QString &filename) |
85 | { |
86 | if (filename.endsWith(".dev" ,PATH_SENSITIVITY)) { |
87 | return FileType::Project; |
88 | } |
89 | if (filename.endsWith(".C" )) { |
90 | return FileType::CppSource; |
91 | } |
92 | if (filename.endsWith(".CPP" )) { |
93 | return FileType::CppSource; |
94 | } |
95 | if (filename.endsWith(".c" ,PATH_SENSITIVITY)) { |
96 | return FileType::CSource; |
97 | } |
98 | if (filename.endsWith(".cpp" ,PATH_SENSITIVITY)) { |
99 | return FileType::CppSource; |
100 | } |
101 | if (filename.endsWith(".cc" ,PATH_SENSITIVITY)) { |
102 | return FileType::CppSource; |
103 | } |
104 | if (filename.endsWith(".cxx" ,PATH_SENSITIVITY)) { |
105 | return FileType::CppSource; |
106 | } |
107 | if (filename.endsWith(".c++" ,PATH_SENSITIVITY)) { |
108 | return FileType::CppSource; |
109 | } |
110 | if (filename.endsWith(".H" )) { |
111 | return FileType::CHeader; |
112 | } |
113 | if (filename.endsWith(".h" ,PATH_SENSITIVITY)) { |
114 | return FileType::CHeader; |
115 | } |
116 | if (filename.endsWith(".hpp" ,PATH_SENSITIVITY)) { |
117 | return FileType::CppHeader; |
118 | } |
119 | if (filename.endsWith(".hh" ,PATH_SENSITIVITY)) { |
120 | return FileType::CppHeader; |
121 | } |
122 | if (filename.endsWith(".hxx" ,PATH_SENSITIVITY)) { |
123 | return FileType::CppHeader; |
124 | } |
125 | if (filename.endsWith(".inl" ,PATH_SENSITIVITY)) { |
126 | return FileType::CppHeader; |
127 | } |
128 | if (filename.endsWith(".rc" ,PATH_SENSITIVITY)) { |
129 | return FileType::WindowsResourceSource; |
130 | } |
131 | if (filename.endsWith(".in" ,PATH_SENSITIVITY)) { |
132 | return FileType::Text; |
133 | } |
134 | if (filename.endsWith(".out" ,PATH_SENSITIVITY)) { |
135 | return FileType::Text; |
136 | } |
137 | if (filename.endsWith(".txt" ,PATH_SENSITIVITY)) { |
138 | return FileType::Text; |
139 | } |
140 | if (filename.endsWith(".dat" ,PATH_SENSITIVITY)) { |
141 | return FileType::Text; |
142 | } |
143 | QMimeDatabase db; |
144 | QMimeType mimeType=db.mimeTypeForFile(filename); |
145 | if (mimeType.isValid() && mimeType.name().startsWith("text/" )) { |
146 | return FileType::Text; |
147 | } |
148 | return FileType::Other; |
149 | } |
150 | |
151 | QString genMakePath(const QString &fileName, bool escapeSpaces, bool encloseInQuotes) |
152 | { |
153 | QString result = fileName; |
154 | |
155 | // Convert backslashes to slashes |
156 | result.replace('\\','/'); |
157 | if (escapeSpaces) { |
158 | result.replace(' ',"\\ " ); |
159 | } |
160 | if (encloseInQuotes) |
161 | if (result.contains(' ')) |
162 | result = '"'+result+'"'; |
163 | return result; |
164 | } |
165 | |
166 | QString genMakePath1(const QString &fileName) |
167 | { |
168 | return genMakePath(fileName, false, true); |
169 | } |
170 | |
171 | QString genMakePath2(const QString &fileName) |
172 | { |
173 | return genMakePath(fileName, true, false); |
174 | } |
175 | |
176 | QString getCompiledExecutableName(const QString& filename) |
177 | { |
178 | return changeFileExt(filename,EXECUTABLE_EXT); |
179 | } |
180 | |
181 | bool programHasConsole(const QString & filename) |
182 | { |
183 | #ifdef Q_OS_WIN |
184 | bool result = false; |
185 | HANDLE handle = CreateFile(filename.toStdWString().c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); |
186 | if (handle != INVALID_HANDLE_VALUE) { |
187 | IMAGE_DOS_HEADER dos_header; |
188 | DWORD signature; |
189 | DWORD bytesread; |
190 | IMAGE_FILE_HEADER pe_header; |
191 | IMAGE_OPTIONAL_HEADER opt_header; |
192 | |
193 | ReadFile(handle, &dos_header, sizeof(dos_header), &bytesread, NULL); |
194 | SetFilePointer(handle, dos_header.e_lfanew, NULL, 0); |
195 | ReadFile(handle, &signature, sizeof(signature), &bytesread, NULL); |
196 | ReadFile(handle, &pe_header, sizeof(pe_header), &bytesread, NULL); |
197 | ReadFile(handle, &opt_header, sizeof(opt_header), &bytesread, NULL); |
198 | |
199 | result = (opt_header.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_CUI); |
200 | } |
201 | CloseHandle(handle); |
202 | return result; |
203 | #else |
204 | return true; |
205 | #endif |
206 | } |
207 | |
208 | QString parseMacros(const QString &s) |
209 | { |
210 | QString result = s; |
211 | Editor *e = pMainWindow->editorList()->getEditor(); |
212 | |
213 | result.replace("<DEFAULT>" , localizePath(QDir::currentPath())); |
214 | result.replace("<DEVCPP>" , localizePath(pSettings->dirs().executable())); |
215 | result.replace("<DEVCPPVERSION>" , REDPANDA_CPP_VERSION); |
216 | result.replace("<EXECPATH>" , localizePath(pSettings->dirs().appDir())); |
217 | QDate today = QDate::currentDate(); |
218 | QDateTime now = QDateTime::currentDateTime(); |
219 | |
220 | result.replace("<DATE>" , today.toString("yyyy-MM-dd" )); |
221 | result.replace("<DATETIME>" , now.toString("yyyy-MM-dd hh:mm:ss" )); |
222 | |
223 | Settings::PCompilerSet compilerSet = pSettings->compilerSets().defaultSet(); |
224 | if (compilerSet) { |
225 | // Only provide the first cpp include dir |
226 | if (compilerSet->defaultCppIncludeDirs().count()>0) |
227 | result.replace("<INCLUDE>" , localizePath(compilerSet->defaultCppIncludeDirs().front())); |
228 | else |
229 | result.replace("<INCLUDE>" ,"" ); |
230 | |
231 | // Only provide the first lib dir |
232 | if (compilerSet->defaultLibDirs().count()>0) |
233 | result.replace("<LIB>" , localizePath(compilerSet->defaultLibDirs().front())); |
234 | else |
235 | result.replace("<LIB>" ,"" ); |
236 | } |
237 | |
238 | if (e!=nullptr && !e->inProject()) { // Non-project editor macros |
239 | result.replace("<EXENAME>" , extractFileName(changeFileExt(e->filename(),EXECUTABLE_EXT))); |
240 | result.replace("<EXEFILE>" , localizePath(changeFileExt(e->filename(),EXECUTABLE_EXT))); |
241 | result.replace("<PROJECTNAME>" , extractFileName(e->filename())); |
242 | result.replace("<PROJECTFILE>" , localizePath(e->filename())); |
243 | result.replace("<PROJECTFILENAME>" , extractFileName(e->filename())); |
244 | result.replace("<PROJECTPATH>" , localizePath(extractFileDir(e->filename()))); |
245 | } else if (pMainWindow->project()) { |
246 | result.replace("<EXENAME>" , extractFileName(pMainWindow->project()->executable())); |
247 | result.replace("<EXEFILE>" , localizePath(pMainWindow->project()->executable())); |
248 | result.replace("<PROJECTNAME>" , pMainWindow->project()->name()); |
249 | result.replace("<PROJECTFILE>" , localizePath(pMainWindow->project()->filename())); |
250 | result.replace("<PROJECTFILENAME>" , extractFileName(pMainWindow->project()->filename())); |
251 | result.replace("<PROJECTPATH>" , localizePath(pMainWindow->project()->directory())); |
252 | } else { |
253 | result.replace("<EXENAME>" , "" ); |
254 | result.replace("<EXEFILE>" , "" ); |
255 | result.replace("<PROJECTNAME>" , "" ); |
256 | result.replace("<PROJECTFILE>" , "" ); |
257 | result.replace("<PROJECTFILENAME>" , "" ); |
258 | result.replace("<PROJECTPATH>" , "" ); |
259 | } |
260 | |
261 | // Editor macros |
262 | if (e!=nullptr) { |
263 | result.replace("<SOURCENAME>" , extractFileName(e->filename())); |
264 | result.replace("<SOURCEFILE>" , localizePath(e->filename())); |
265 | result.replace("<SOURCEPATH>" , localizePath(extractFileDir(e->filename()))); |
266 | result.replace("<WORDXY>" , e->wordAtCursor()); |
267 | } else { |
268 | result.replace("<SOURCENAME>" , "" ); |
269 | result.replace("<SOURCEFILE>" , "" ); |
270 | result.replace("<SOURCEPATH>" , "" ); |
271 | result.replace("<WORDXY>" , "" ); |
272 | } |
273 | return result; |
274 | } |
275 | |
276 | void resetCppParser(std::shared_ptr<CppParser> parser, int compilerSetIndex) |
277 | { |
278 | if (!parser) |
279 | return; |
280 | // Configure parser |
281 | parser->reset(); |
282 | //paser->enabled = pSettings-> devCodeCompletion.Enabled; |
283 | // CppParser.ParseLocalHeaders := devCodeCompletion.ParseLocalHeaders; |
284 | // CppParser.ParseGlobalHeaders := devCodeCompletion.ParseGlobalHeaders; |
285 | parser->setEnabled(true); |
286 | parser->setParseGlobalHeaders(true); |
287 | parser->setParseLocalHeaders(true); |
288 | // Set options depending on the current compiler set |
289 | // TODO: do this every time OnCompilerSetChanged |
290 | if (compilerSetIndex<0) { |
291 | compilerSetIndex=pSettings->compilerSets().defaultIndex(); |
292 | } |
293 | Settings::PCompilerSet compilerSet = pSettings->compilerSets().getSet(compilerSetIndex); |
294 | parser->clearIncludePaths(); |
295 | if (compilerSet) { |
296 | foreach (const QString& file,compilerSet->CppIncludeDirs()) { |
297 | parser->addIncludePath(file); |
298 | } |
299 | foreach (const QString& file,compilerSet->CIncludeDirs()) { |
300 | parser->addIncludePath(file); |
301 | } |
302 | foreach (const QString& file,compilerSet->defaultCppIncludeDirs()) { |
303 | parser->addIncludePath(file); |
304 | } |
305 | foreach (const QString& file,compilerSet->defaultCIncludeDirs()) { |
306 | parser->addIncludePath(file); |
307 | } |
308 | //TODO: Add default include dirs last, just like gcc does |
309 | // Set defines |
310 | if (parser->language()==ParserLanguage::C) { |
311 | for (QString define:compilerSet->CDefines()) { |
312 | parser->addHardDefineByLine(define); // predefined constants from -dM -E |
313 | } |
314 | } else { |
315 | for (QString define:compilerSet->CppDefines()) { |
316 | parser->addHardDefineByLine(define); // predefined constants from -dM -E |
317 | } |
318 | } |
319 | // add a Red Pand C++ 's own macro |
320 | parser->addHardDefineByLine("#define EGE_FOR_AUTO_CODE_COMPLETETION_ONLY" ); |
321 | // add C/C++ default macro |
322 | parser->addHardDefineByLine("#define __FILE__ 1" ); |
323 | parser->addHardDefineByLine("#define __LINE__ 1" ); |
324 | parser->addHardDefineByLine("#define __DATE__ 1" ); |
325 | parser->addHardDefineByLine("#define __TIME__ 1" ); |
326 | } |
327 | parser->parseHardDefines(); |
328 | pMainWindow->disconnect(parser.get(), |
329 | &CppParser::onStartParsing, |
330 | pMainWindow, |
331 | &MainWindow::onStartParsing); |
332 | pMainWindow->disconnect(parser.get(), |
333 | &CppParser::onProgress, |
334 | pMainWindow, |
335 | &MainWindow::onParserProgress); |
336 | pMainWindow->disconnect(parser.get(), |
337 | &CppParser::onEndParsing, |
338 | pMainWindow, |
339 | &MainWindow::onEndParsing); |
340 | pMainWindow->connect(parser.get(), |
341 | &CppParser::onStartParsing, |
342 | pMainWindow, |
343 | &MainWindow::onStartParsing); |
344 | pMainWindow->connect(parser.get(), |
345 | &CppParser::onProgress, |
346 | pMainWindow, |
347 | &MainWindow::onParserProgress); |
348 | pMainWindow->connect(parser.get(), |
349 | &CppParser::onEndParsing, |
350 | pMainWindow, |
351 | &MainWindow::onEndParsing); |
352 | } |
353 | |
354 | int getNewFileNumber() |
355 | { |
356 | static int count = 0; |
357 | count++; |
358 | return count; |
359 | } |
360 | |
361 | #ifdef Q_OS_WIN |
362 | static bool gIsGreenEdition = true; |
363 | static bool gIsGreenEditionInited = false; |
364 | #endif |
365 | bool isGreenEdition() |
366 | { |
367 | #ifdef Q_OS_WIN |
368 | if (!gIsGreenEditionInited) { |
369 | QString keyString = QString("Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\RedPanda-C++" ); |
370 | QString value; |
371 | if (!readRegistry(HKEY_LOCAL_MACHINE,keyString.toLocal8Bit(),"UninstallString" ,value)) { |
372 | keyString = "SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\RedPanda-C++" ; |
373 | if (!readRegistry(HKEY_LOCAL_MACHINE,keyString.toLocal8Bit(),"UninstallString" ,value)) { |
374 | value="" ; |
375 | } |
376 | } |
377 | if (!value.isEmpty()) { |
378 | QString regPath = extractFileDir(value); |
379 | |
380 | QString appPath = QApplication::instance()->applicationDirPath(); |
381 | gIsGreenEdition = excludeTrailingPathDelimiter(regPath).compare(excludeTrailingPathDelimiter(appPath), |
382 | Qt::CaseInsensitive)!=0; |
383 | } |
384 | gIsGreenEditionInited = true; |
385 | } |
386 | return gIsGreenEdition; |
387 | #else |
388 | return false; |
389 | #endif |
390 | } |
391 | |
392 | QByteArray runAndGetOutput(const QString &cmd, const QString& workingDir, const QStringList& arguments, |
393 | const QByteArray &inputContent, bool inheritEnvironment, |
394 | const QProcessEnvironment& env) |
395 | { |
396 | QProcess process; |
397 | QByteArray result; |
398 | if (env.isEmpty()) { |
399 | if (inheritEnvironment) { |
400 | process.setProcessEnvironment(QProcessEnvironment::systemEnvironment()); |
401 | } else { |
402 | process.setProcessEnvironment(QProcessEnvironment()); |
403 | } |
404 | } else { |
405 | process.setProcessEnvironment(env); |
406 | } |
407 | process.setWorkingDirectory(workingDir); |
408 | process.connect(&process,&QProcess::readyReadStandardError, |
409 | [&](){ |
410 | result.append(process.readAllStandardError()); |
411 | }); |
412 | process.connect(&process,&QProcess::readyReadStandardOutput, |
413 | [&](){ |
414 | result.append(process.readAllStandardOutput()); |
415 | }); |
416 | process.start(cmd,arguments); |
417 | if (!inputContent.isEmpty()) { |
418 | process.write(inputContent); |
419 | } |
420 | process.closeWriteChannel(); |
421 | process.waitForFinished(); |
422 | return result; |
423 | } |
424 | |
425 | void executeFile(const QString &fileName, const QString ¶ms, const QString &workingDir, const QString &tempFile) |
426 | { |
427 | ExecutableRunner* runner=new ExecutableRunner( |
428 | fileName, |
429 | params, |
430 | workingDir); |
431 | runner->connect(runner, &QThread::finished, |
432 | [runner,tempFile](){ |
433 | if (!tempFile.isEmpty()) { |
434 | QFile::remove(tempFile); |
435 | } |
436 | runner->deleteLater(); |
437 | }); |
438 | runner->connect(runner, &Runner::runErrorOccurred, |
439 | [](const QString&){ |
440 | //todo |
441 | }); |
442 | runner->setStartConsole(true); |
443 | runner->start(); |
444 | } |
445 | |
446 | #ifdef Q_OS_WIN |
447 | bool readRegistry(HKEY key,const QByteArray& subKey, const QByteArray& name, QString& value) { |
448 | DWORD dataSize; |
449 | LONG result; |
450 | result = RegGetValueA(key,subKey, |
451 | name, RRF_RT_REG_SZ | RRF_RT_REG_MULTI_SZ, |
452 | NULL, |
453 | NULL, |
454 | &dataSize); |
455 | if (result!=ERROR_SUCCESS) |
456 | return false; |
457 | char * buffer = new char[dataSize+10]; |
458 | result = RegGetValueA(key,subKey, |
459 | name, RRF_RT_REG_SZ | RRF_RT_REG_MULTI_SZ, |
460 | NULL, |
461 | buffer, |
462 | &dataSize); |
463 | if (result!=ERROR_SUCCESS) { |
464 | delete[] buffer; |
465 | return false; |
466 | } |
467 | value=QString::fromLocal8Bit(buffer); |
468 | delete [] buffer; |
469 | return true; |
470 | } |
471 | #endif |
472 | |
473 | qulonglong stringToHex(const QString &str, bool &isOk) |
474 | { |
475 | qulonglong value = str.toULongLong(&isOk,16); |
476 | return value; |
477 | } |
478 | |
479 | bool findComplement(const QString &s, const QChar &fromToken, const QChar &toToken, int &curPos, int increment) |
480 | { |
481 | int curPosBackup = curPos; |
482 | int level = 0; |
483 | //todo: skip comment, char and strings |
484 | while ((curPos < s.length()) && (curPos >= 0)) { |
485 | if (s[curPos] == fromToken) { |
486 | level++; |
487 | } else if (s[curPos] == toToken) { |
488 | level--; |
489 | if (level == 0) |
490 | return true; |
491 | } |
492 | curPos += increment; |
493 | } |
494 | curPos = curPosBackup; |
495 | return false; |
496 | } |
497 | |
498 | bool haveGoodContrast(const QColor& c1, const QColor &c2) { |
499 | int lightness1 = qGray(c1.rgb()); |
500 | int lightness2 = qGray(c2.rgb()); |
501 | return std::abs(lightness1 - lightness2)>=120; |
502 | } |
503 | |
504 | QByteArray getHTTPBody(const QByteArray& content) { |
505 | int i= content.indexOf("\r\n\r\n" ); |
506 | if (i>=0) { |
507 | return content.mid(i+4); |
508 | } |
509 | return "" ; |
510 | } |
511 | |
512 | QString getSizeString(int size) |
513 | { |
514 | if (size < 1024) { |
515 | return QString("%1 " ).arg(size)+QObject::tr("bytes" ); |
516 | } else if (size < 1024 * 1024) { |
517 | return QString("%1 " ).arg(size / 1024.0)+QObject::tr("KB" ); |
518 | } else if (size < 1024 * 1024 * 1024) { |
519 | return QString("%1 " ).arg(size / 1024.0 / 1024.0)+QObject::tr("MB" ); |
520 | } else { |
521 | return QString("%1 " ).arg(size / 1024.0 / 1024.0 / 1024.0)+QObject::tr("GB" ); |
522 | } |
523 | } |
524 | |