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
18QStringList 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
84FileType 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
151QString 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
166QString genMakePath1(const QString &fileName)
167{
168 return genMakePath(fileName, false, true);
169}
170
171QString genMakePath2(const QString &fileName)
172{
173 return genMakePath(fileName, true, false);
174}
175
176QString getCompiledExecutableName(const QString& filename)
177{
178 return changeFileExt(filename,EXECUTABLE_EXT);
179}
180
181bool 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
208QString 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
276void 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
354int getNewFileNumber()
355{
356 static int count = 0;
357 count++;
358 return count;
359}
360
361#ifdef Q_OS_WIN
362static bool gIsGreenEdition = true;
363static bool gIsGreenEditionInited = false;
364#endif
365bool 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
392QByteArray 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
425void executeFile(const QString &fileName, const QString &params, 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
447bool 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
473qulonglong stringToHex(const QString &str, bool &isOk)
474{
475 qulonglong value = str.toULongLong(&isOk,16);
476 return value;
477}
478
479bool 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
498bool 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
504QByteArray 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
512QString 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