#include "utils.h" #include "systemconsts.h" #include #include #include #include #include #include #include #include #include #include "editor.h" #include "editorlist.h" #include "settings.h" #include "mainwindow.h" #include "project.h" #include "parser/cppparser.h" #include "compiler/executablerunner.h" #include #include "utils/escape.h" #include "utils/parsearg.h" #ifdef Q_OS_WIN #include #include #endif #ifdef Q_OS_WIN using pIsWow64Process2_t = BOOL (WINAPI *)( HANDLE hProcess, USHORT *pProcessMachine, USHORT *pNativeMachine ); #endif NonExclusiveTemporaryFileOwner::NonExclusiveTemporaryFileOwner(std::unique_ptr &tempFile) : filename(tempFile ? tempFile->fileName() : QString()) { if (tempFile) { tempFile->flush(); tempFile->setAutoRemove(false); tempFile = nullptr; } } NonExclusiveTemporaryFileOwner::~NonExclusiveTemporaryFileOwner() { if (!filename.isEmpty()) QFile::remove(filename); } FileType getFileType(const QString &filename) { if (filename.endsWith(".s",PATH_SENSITIVITY)) { return FileType::GAS; } if (filename.endsWith(".S",PATH_SENSITIVITY)) { return FileType::GAS; } if (filename.endsWith(".dev",PATH_SENSITIVITY)) { return FileType::Project; } if (filename.endsWith(".C")) { return FileType::CppSource; } if (filename.endsWith(".CPP")) { return FileType::CppSource; } if (filename.endsWith(".c",PATH_SENSITIVITY)) { return FileType::CSource; } if (filename.endsWith(".cpp",PATH_SENSITIVITY)) { return FileType::CppSource; } if (filename.endsWith(".cc",PATH_SENSITIVITY)) { return FileType::CppSource; } if (filename.endsWith(".cxx",PATH_SENSITIVITY)) { return FileType::CppSource; } if (filename.endsWith(".c++",PATH_SENSITIVITY)) { return FileType::CppSource; } if (filename.endsWith(".H")) { return FileType::CHeader; } if (filename.endsWith(".h",PATH_SENSITIVITY)) { return FileType::CHeader; } if (filename.endsWith(".hpp",PATH_SENSITIVITY)) { return FileType::CppHeader; } if (filename.endsWith(".hh",PATH_SENSITIVITY)) { return FileType::CppHeader; } if (filename.endsWith(".hxx",PATH_SENSITIVITY)) { return FileType::CppHeader; } if (filename.endsWith(".inl",PATH_SENSITIVITY)) { return FileType::CppHeader; } if (filename.endsWith(".rc",PATH_SENSITIVITY)) { return FileType::WindowsResourceSource; } if (filename.endsWith(".in",PATH_SENSITIVITY)) { return FileType::Text; } if (filename.endsWith(".out",PATH_SENSITIVITY)) { return FileType::Text; } if (filename.endsWith(".txt",PATH_SENSITIVITY)) { return FileType::Text; } if (filename.endsWith(".md",PATH_SENSITIVITY)) { return FileType::Text; } if (filename.endsWith(".info",PATH_SENSITIVITY)) { return FileType::Text; } if (filename.endsWith(".dat",PATH_SENSITIVITY)) { return FileType::Text; } if (filename.endsWith(".lua",PATH_SENSITIVITY)) { return FileType::LUA; } if (filename.endsWith(".fs",PATH_SENSITIVITY)) { return FileType::FragmentShader; } if (filename.endsWith(".vs",PATH_SENSITIVITY)) { return FileType::VerticeShader; } if (filename.endsWith(".def",PATH_SENSITIVITY)) { return FileType::ModuleDef; } QFileInfo info(filename); if (info.suffix().isEmpty()) { return FileType::Other; } return FileType::Other; } bool programHasConsole(const QString & filename) { #ifdef Q_OS_WIN bool result = false; HANDLE handle = CreateFileW(filename.toStdWString().c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); if (handle != INVALID_HANDLE_VALUE) { IMAGE_DOS_HEADER dos_header; DWORD signature; DWORD bytesread; IMAGE_FILE_HEADER pe_header; IMAGE_OPTIONAL_HEADER opt_header; ReadFile(handle, &dos_header, sizeof(dos_header), &bytesread, NULL); SetFilePointer(handle, dos_header.e_lfanew, NULL, 0); ReadFile(handle, &signature, sizeof(signature), &bytesread, NULL); ReadFile(handle, &pe_header, sizeof(pe_header), &bytesread, NULL); ReadFile(handle, &opt_header, sizeof(opt_header), &bytesread, NULL); result = (opt_header.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_CUI); } CloseHandle(handle); return result; #else return true; #endif } QString parseMacros(const QString &s) { return parseMacros(s, devCppMacroVariables()); } QString parseMacros(const QString &s, const QMap ¯os) { QString result = s; for (auto it = macros.begin(); it != macros.end(); ++it) { QString key = it.key(); QString value = it.value(); result.replace('<' + key + '>', value); } return result; } QMap devCppMacroVariables() { Editor *e = pMainWindow->editorList()->getEditor(); QMap result = { {"DEFAULT", localizePath(QDir::currentPath())}, {"DEVCPP", localizePath(pSettings->dirs().executable())}, {"DEVCPPVERSION", REDPANDA_CPP_VERSION}, {"EXECPATH", localizePath(pSettings->dirs().appDir())}, {"DATE", QDate::currentDate().toString("yyyy-MM-dd")}, {"DATETIME", QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss")} }; Settings::PCompilerSet compilerSet = pSettings->compilerSets().defaultSet(); if (compilerSet) { // Only provide the first cpp include dir if (compilerSet->defaultCppIncludeDirs().count() > 0) result["INCLUDE"] = localizePath(compilerSet->defaultCppIncludeDirs().front()); else result["INCLUDE"] = ""; // Only provide the first lib dir if (compilerSet->defaultLibDirs().count() > 0) result["LIB"] = localizePath(compilerSet->defaultLibDirs().front()); else result["LIB"] = ""; } if (e != nullptr && !e->inProject()) { // Non-project editor macros QString exeSuffix; Settings::PCompilerSet compilerSet = pSettings->compilerSets().defaultSet(); if (compilerSet) { exeSuffix = compilerSet->executableSuffix(); } else { exeSuffix = DEFAULT_EXECUTABLE_SUFFIX; } result["EXENAME"] = extractFileName(changeFileExt(e->filename(), exeSuffix)); result["EXEFILE"] = localizePath(changeFileExt(e->filename(), exeSuffix)); result["PROJECTNAME"] = extractFileName(e->filename()); result["PROJECTFILE"] = localizePath(e->filename()); result["PROJECTFILENAME"] = extractFileName(e->filename()); result["PROJECTPATH"] = localizePath(extractFileDir(e->filename())); } else if (pMainWindow->project()) { result["EXENAME"] = extractFileName(pMainWindow->project()->outputFilename()); result["EXEFILE"] = localizePath(pMainWindow->project()->outputFilename()); result["PROJECTNAME"] = pMainWindow->project()->name(); result["PROJECTFILE"] = localizePath(pMainWindow->project()->filename()); result["PROJECTFILENAME"] = extractFileName(pMainWindow->project()->filename()); result["PROJECTPATH"] = localizePath(pMainWindow->project()->directory()); } else { result["EXENAME"] = ""; result["EXEFILE"] = ""; result["PROJECTNAME"] = ""; result["PROJECTFILE"] = ""; result["PROJECTFILENAME"] = ""; result["PROJECTPATH"] = ""; } // Editor macros if (e != nullptr) { result["SOURCENAME"] = extractFileName(e->filename()); result["SOURCEFILE"] = localizePath(e->filename()); result["SOURCEPATH"] = localizePath(extractFileDir(e->filename())); result["WORDXY"] = e->wordAtCursor(); } else { result["SOURCENAME"] = ""; result["SOURCEFILE"] = ""; result["SOURCEPATH"] = ""; result["WORDXY"] = ""; } return result; } void resetCppParser(std::shared_ptr parser, int compilerSetIndex) { if (!parser) return; // Configure parser parser->resetParser(); parser->setEnabled(true); parser->setParseGlobalHeaders(true); parser->setParseLocalHeaders(true); // Set options depending on the current compiler set if (compilerSetIndex<0) { compilerSetIndex=pSettings->compilerSets().defaultIndex(); } Settings::PCompilerSet compilerSet = pSettings->compilerSets().getSet(compilerSetIndex); #ifdef ENABLE_SDCC if (compilerSet && compilerSet->compilerType()==CompilerType::SDCC) parser->setLanguage(ParserLanguage::SDCC); #endif parser->clearIncludePaths(); bool isCpp = parser->language()==ParserLanguage::CPlusPlus; if (compilerSet) { if (isCpp) { foreach (const QString& file,compilerSet->CppIncludeDirs()) { parser->addIncludePath(file); } } foreach (const QString& file,compilerSet->CIncludeDirs()) { parser->addIncludePath(file); } if (isCpp) { foreach (const QString& file,compilerSet->defaultCppIncludeDirs()) { parser->addIncludePath(file); } } foreach (const QString& file,compilerSet->defaultCIncludeDirs()) { parser->addIncludePath(file); } // Set defines for (QString define:compilerSet->defines(parser->language()==ParserLanguage::CPlusPlus)) { parser->addHardDefineByLine(define); } // // add a Red Pand C++ 's own macro // parser->addHardDefineByLine("#define EGE_FOR_AUTO_CODE_COMPLETETION_ONLY"); // add C/C++ default macro parser->addHardDefineByLine("#define __FILE__ 1"); parser->addHardDefineByLine("#define __LINE__ 1"); parser->addHardDefineByLine("#define __DATE__ 1"); parser->addHardDefineByLine("#define __TIME__ 1"); } parser->parseHardDefines(); pMainWindow->disconnect(parser.get(), &CppParser::onStartParsing, pMainWindow, &MainWindow::onStartParsing); pMainWindow->disconnect(parser.get(), &CppParser::onProgress, pMainWindow, &MainWindow::onParserProgress); pMainWindow->disconnect(parser.get(), &CppParser::onEndParsing, pMainWindow, &MainWindow::onEndParsing); pMainWindow->connect(parser.get(), &CppParser::onStartParsing, pMainWindow, &MainWindow::onStartParsing); pMainWindow->connect(parser.get(), &CppParser::onProgress, pMainWindow, &MainWindow::onParserProgress); pMainWindow->connect(parser.get(), &CppParser::onEndParsing, pMainWindow, &MainWindow::onEndParsing); } int getNewFileNumber() { static int count = 0; count++; return count; } #ifdef Q_OS_WIN static bool gIsGreenEdition = true; static bool gIsGreenEditionInited = false; bool isGreenEdition() { if (!gIsGreenEditionInited) { QString appPath = QApplication::instance()->applicationDirPath(); appPath = excludeTrailingPathDelimiter(localizePath(appPath)); QString keyString = R"(Software\Microsoft\Windows\CurrentVersion\Uninstall\RedPanda-C++)"; QString systemInstallPath; readRegistry(HKEY_LOCAL_MACHINE, keyString, "InstallLocation", systemInstallPath); if (systemInstallPath.isEmpty()) { readRegistry(HKEY_LOCAL_MACHINE, keyString, "UninstallString", systemInstallPath); systemInstallPath = excludeTrailingPathDelimiter(extractFileDir(systemInstallPath)); } else systemInstallPath = excludeTrailingPathDelimiter(systemInstallPath); QString userInstallPath; readRegistry(HKEY_CURRENT_USER, keyString, "InstallLocation", userInstallPath); if (userInstallPath.isEmpty()) { readRegistry(HKEY_CURRENT_USER, keyString, "UninstallString", userInstallPath); userInstallPath = excludeTrailingPathDelimiter(extractFileDir(userInstallPath)); } else userInstallPath = excludeTrailingPathDelimiter(userInstallPath); systemInstallPath = localizePath(systemInstallPath); userInstallPath = localizePath(userInstallPath); gIsGreenEdition = appPath.compare(systemInstallPath, Qt::CaseInsensitive) != 0 && appPath.compare(userInstallPath, Qt::CaseInsensitive) != 0; gIsGreenEditionInited = true; } return gIsGreenEdition; } #endif ProcessOutput runAndGetOutput(const QString &cmd, const QString& workingDir, const QStringList& arguments, const QByteArray &inputContent, bool separateStderr, bool inheritEnvironment, const QProcessEnvironment& env) { QProcess process; QByteArray standardOutput; QByteArray standardError; QString errorMessage; bool errorOccurred = false; if (env.isEmpty()) { if (inheritEnvironment) { process.setProcessEnvironment(QProcessEnvironment::systemEnvironment()); } else { process.setProcessEnvironment(QProcessEnvironment()); } } else { process.setProcessEnvironment(env); } if (separateStderr) process.setProcessChannelMode(QProcess::SeparateChannels); else process.setProcessChannelMode(QProcess::MergedChannels); process.setReadChannel(QProcess::StandardOutput); process.setWorkingDirectory(workingDir); process.connect(&process,&QProcess::readyReadStandardOutput, [&](){ standardOutput.append(process.readAllStandardOutput()); }); if (separateStderr) process.connect(&process, &QProcess::readyReadStandardError, [&]() { standardError.append(process.readAllStandardError()); }); process.connect(&process, &QProcess::errorOccurred, [&](){ errorOccurred= true; }); process.start(cmd,arguments); if (!inputContent.isEmpty()) { process.write(inputContent); } process.closeWriteChannel(); process.waitForFinished(); if (errorOccurred) { switch(process.error()) { case QProcess::FailedToStart: errorMessage += "Failed to start process!"; break; case QProcess::Crashed: errorMessage += "Process crashed!"; break; case QProcess::Timedout: errorMessage += "Timeout!"; break; case QProcess::ReadError: errorMessage += "Read Error:"; break; case QProcess::WriteError: errorMessage += "Write Error:"; break; case QProcess::UnknownError: errorMessage += "Unknown Error:"; break; } errorMessage += process.errorString(); } return {standardOutput, standardError, errorMessage}; } void executeFile(const QString &fileName, const QStringList ¶ms, const QString &workingDir, const QString &tempFile) { ExecutableRunner* runner=new ExecutableRunner( fileName, params, workingDir); runner->connect(runner, &QThread::finished, [runner,tempFile](){ if (!tempFile.isEmpty()) { QFile::remove(tempFile); } runner->deleteLater(); }); runner->connect(runner, &Runner::runErrorOccurred, [](const QString&){ //todo }); runner->setStartConsole(true); runner->start(); } #ifdef Q_OS_WIN bool readRegistry(HKEY key,const QString& subKey, const QString& name, QString& value) { LONG result; HKEY hkey; result = RegOpenKeyExW(key, subKey.toStdWString().c_str(), 0, KEY_READ, &hkey); if (result != ERROR_SUCCESS) return false; DWORD dataType; DWORD dataSize; result = RegQueryValueExW(hkey, name.toStdWString().c_str(), NULL, &dataType, NULL, &dataSize); if (result != ERROR_SUCCESS || (dataType != REG_SZ && dataType != REG_MULTI_SZ)) { RegCloseKey(hkey); return false; } wchar_t * buffer = new wchar_t[dataSize / sizeof(wchar_t) + 10]; result = RegQueryValueExW(hkey, name.toStdWString().c_str(), NULL, &dataType, (LPBYTE)buffer, &dataSize); RegCloseKey(hkey); if (result != ERROR_SUCCESS) { delete[] buffer; return false; } value = QString::fromWCharArray(buffer); delete [] buffer; return true; } #endif qulonglong stringToHex(const QString &str, bool &isOk) { qulonglong value = str.toULongLong(&isOk,16); return value; } bool findComplement(const QString &s, const QChar &fromToken, const QChar &toToken, int &curPos, int increment) { int curPosBackup = curPos; int level = 0; //todo: skip comment, char and strings while ((curPos < s.length()) && (curPos >= 0)) { if (s[curPos] == fromToken) { level++; } else if (s[curPos] == toToken) { level--; if (level == 0) return true; } curPos += increment; } curPos = curPosBackup; return false; } bool haveGoodContrast(const QColor& c1, const QColor &c2) { int lightness1 = qGray(c1.rgb()); int lightness2 = qGray(c2.rgb()); return std::abs(lightness1 - lightness2)>=120; } QByteArray getHTTPBody(const QByteArray& content) { int i= content.indexOf("\r\n\r\n"); if (i>=0) { return content.mid(i+4); } return ""; } QString getSizeString(int size) { if (size < 1024) { return QString("%1 ").arg(size)+QObject::tr("bytes"); } else if (size < 1024 * 1024) { return QString("%1 ").arg(size / 1024.0,0,'f',2)+QObject::tr("KB"); } else if (size < 1024 * 1024 * 1024) { return QString("%1 ").arg(size / 1024.0 / 1024.0)+QObject::tr("MB"); } else { return QString("%1 ").arg(size / 1024.0 / 1024.0 / 1024.0)+QObject::tr("GB"); } } void saveComboHistory(QComboBox* cb,const QString& text) { QString s = text; if (s.isEmpty()) return; int i = cb->findText(s); if (i>=0) { cb->removeItem(i); } cb->insertItem(0,s); cb->setCurrentText(s); } void openFileFolderInExplorer(const QString &path) { QFileInfo info(path); if (info.isFile()){ #ifdef Q_OS_WIN QProcess process; QStringList args; QString filepath=QDir::toNativeSeparators(info.absoluteFilePath()); args.append("/n,"); args.append("/select,"); args.append(QString("%1").arg(filepath)); process.startDetached("explorer.exe",args); #else QDesktopServices::openUrl( QUrl("file:///"+ includeTrailingPathDelimiter(info.path()),QUrl::TolerantMode)); #endif } else if (info.isDir()){ QDesktopServices::openUrl( QUrl("file:///"+ includeTrailingPathDelimiter(path),QUrl::TolerantMode)); } } QColor alphaBlend(const QColor &lower, const QColor &upper) { qreal wu = upper.alphaF(); // weight of upper color qreal wl = 1 - wu; // weight of lower color return QColor( int(lower.red() * wl + upper.red() * wu), int(lower.green() * wl + upper.green() * wu), int(lower.blue() * wl + upper.blue() * wu) ); } QStringList getExecutableSearchPaths() { QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); QString path = env.value("PATH"); QStringList pathList = path.split(PATH_SEPARATOR); #ifdef Q_OS_WINDOWS /* follow Windows `CreateProcessW` search semantics. * ref. https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessw . */ QStringList searchList{}; wchar_t buffer[MAX_PATH]; // 1. the directory from which the application loaded searchList.push_back(QApplication::instance()->applicationDirPath()); // 2. the current directory for the parent process // here we add it because launching from GUI the current directory is relatively stable searchList.push_back(QDir::currentPath()); // 3. the 32-bit Windows system directory if (GetSystemDirectoryW(buffer, MAX_PATH) > 0) searchList.push_back(QString::fromWCharArray(buffer)); if (GetWindowsDirectoryW(buffer, MAX_PATH) > 0) { // 4. the 16-bit Windows system directory searchList.push_back(QString::fromWCharArray(buffer) + "/System"); // 5. the Windows directory searchList.push_back(QString::fromWCharArray(buffer)); } // 6. the directories that are listed in the PATH environment variable searchList.append(pathList); return searchList; #else return pathList; #endif } QStringList platformCommandForTerminalArgsPreview() { #ifdef Q_OS_WINDOWS QVersionNumber currentVersion = QVersionNumber::fromString(QSysInfo::kernelVersion()); if (currentVersion >= QVersionNumber(6, 1)) return {"powershell.exe", "-c", "echo hello; sleep 3"}; else return {"cmd.exe", "/c", "echo hello & ping 127.0.0.1"}; #else return {"sh", "-c", "echo hello; sleep 3"}; #endif } QString appArch() { #ifdef _M_ARM64EC return "arm64ec"; #else return QSysInfo::buildCpuArchitecture(); #endif } QString osArch() { #ifdef Q_OS_WINDOWS pIsWow64Process2_t pIsWow64Process2 = reinterpret_cast(GetProcAddress(GetModuleHandleW(L"kernel32"), "IsWow64Process2")); if (pIsWow64Process2) { // IsWow64Process2 returns real native architecture under xtajit, while GetNativeSystemInfo does not. USHORT processMachineResult, nativeMachineResult; if (pIsWow64Process2(GetCurrentProcess(), &processMachineResult, &nativeMachineResult)) switch (nativeMachineResult) { case IMAGE_FILE_MACHINE_I386: return "i386"; case IMAGE_FILE_MACHINE_AMD64: return "x86_64"; case IMAGE_FILE_MACHINE_ARM64: return "arm64"; } return QSysInfo::currentCpuArchitecture(); } else return QSysInfo::currentCpuArchitecture(); #else return QSysInfo::currentCpuArchitecture(); #endif } QString byteArrayToString(const QByteArray &content, bool isUTF8) { if (isUTF8) return QString::fromUtf8(content); else return QString::fromLocal8Bit(content); } QByteArray stringToByteArray(const QString &content, bool isUTF8) { if (isUTF8) return content.toUtf8(); else return content.toLocal8Bit(); } std::tuple wrapCommandForTerminalEmulator(const QString &terminal, const QStringList &argsPattern, const QStringList &payloadArgsWithArgv0) { QStringList wrappedArgs; std::unique_ptr temproryFile; for (const QString &patternItem : argsPattern) { if (patternItem == "$term") wrappedArgs.append(terminal); else if (patternItem == "$integrated_term") wrappedArgs.append(includeTrailingPathDelimiter(pSettings->dirs().appDir())+terminal); else if (patternItem == "$argv") wrappedArgs.append(payloadArgsWithArgv0); else if (patternItem == "$command" || patternItem == "$unix_command") { // “$command” is for compatibility; previously used on multiple Unix terms QStringList escapedArgs; for (int i = 0; i < payloadArgsWithArgv0.length(); i++) { auto &arg = payloadArgsWithArgv0[i]; auto escaped = escapeArgument(arg, i == 0, EscapeArgumentRule::BourneAgainShellPretty); escapedArgs.append(escaped); } wrappedArgs.push_back(escapedArgs.join(' ')); } else if (patternItem == "$dos_command") { QStringList escapedArgs; for (int i = 0; i < payloadArgsWithArgv0.length(); i++) { auto &arg = payloadArgsWithArgv0[i]; auto escaped = escapeArgument(arg, i == 0, EscapeArgumentRule::WindowsCommandPrompt); escapedArgs.append(escaped); } wrappedArgs.push_back(escapedArgs.join(' ')); } else if (patternItem == "$lpCommandLine") { QStringList escapedArgs; for (int i = 0; i < payloadArgsWithArgv0.length(); i++) { auto &arg = payloadArgsWithArgv0[i]; auto escaped = escapeArgument(arg, i == 0, EscapeArgumentRule::WindowsCreateProcess); escapedArgs.append(escaped); } wrappedArgs.push_back(escapedArgs.join(' ')); } else if (patternItem == "$tmpfile" || patternItem == "$tmpfile.command") { // “$tmpfile” is for compatibility; previously used on macOS Terminal.app temproryFile = std::make_unique(QDir::tempPath() + "/redpanda_XXXXXX.command"); if (temproryFile->open()) { QStringList escapedArgs; for (int i = 0; i < payloadArgsWithArgv0.length(); i++) { auto &arg = payloadArgsWithArgv0[i]; auto escaped = escapeArgument(arg, i == 0, EscapeArgumentRule::BourneAgainShellPretty); escapedArgs.append(escaped); } temproryFile->write(escapedArgs.join(' ').toUtf8()); temproryFile->write("\n"); QFile(temproryFile->fileName()).setPermissions(QFileDevice::ReadOwner | QFileDevice::WriteOwner | QFileDevice::ExeOwner); } wrappedArgs.push_back(temproryFile->fileName()); } else if (patternItem == "$tmpfile.sh") { temproryFile = std::make_unique(QDir::tempPath() + "/redpanda_XXXXXX.command"); if (temproryFile->open()) { QStringList escapedArgs = {"exec"}; for (int i = 0; i < payloadArgsWithArgv0.length(); i++) { auto &arg = payloadArgsWithArgv0[i]; auto escaped = escapeArgument(arg, false, EscapeArgumentRule::BourneAgainShellPretty); escapedArgs.append(escaped); } temproryFile->write("#!/bin/sh\n"); temproryFile->write(escapedArgs.join(' ').toUtf8()); temproryFile->write("\n"); QFile(temproryFile->fileName()).setPermissions(QFileDevice::ReadOwner | QFileDevice::WriteOwner | QFileDevice::ExeOwner); } wrappedArgs.push_back(temproryFile->fileName()); } else if (patternItem == "$tmpfile.bat") { temproryFile = std::make_unique(QDir::tempPath() + "/redpanda_XXXXXX.bat"); if (temproryFile->open()) { QStringList escapedArgs; for (int i = 0; i < payloadArgsWithArgv0.length(); i++) { auto &arg = payloadArgsWithArgv0[i]; auto escaped = escapeArgument(arg, i == 0, EscapeArgumentRule::WindowsCommandPrompt); escapedArgs.append(escaped); } temproryFile->write(escapedArgs.join(' ').toLocal8Bit()); temproryFile->write("\r\n"); QFile(temproryFile->fileName()).setPermissions(QFileDevice::ReadOwner | QFileDevice::WriteOwner | QFileDevice::ExeOwner); } wrappedArgs.push_back(temproryFile->fileName()); } else if (patternItem == "$sequential_app_id") { static QString prefix = QStringLiteral("io.redpanda.term_%1_").arg(QCoreApplication::applicationPid()); static std::atomic appIdCounter = 0; QString appId = prefix + QString::number(++appIdCounter); wrappedArgs.push_back(appId); } else wrappedArgs.push_back(patternItem); } if (wrappedArgs.empty()) return {QString(""), QStringList{}, std::make_unique(temproryFile)}; return {wrappedArgs[0], wrappedArgs.mid(1), std::make_unique(temproryFile)}; } std::tuple wrapCommandForTerminalEmulator(const QString &terminal, const QString &argsPattern, const QStringList &payloadArgsWithArgv0) { return wrapCommandForTerminalEmulator(terminal, parseArguments(argsPattern, Settings::Environment::terminalArgsPatternMagicVariables(), false), payloadArgsWithArgv0); } ExternalResource::ExternalResource() { Q_INIT_RESOURCE(qsynedit_qmake_qmake_qm_files); Q_INIT_RESOURCE(redpanda_qt_utils_qmake_qmake_qm_files); } ExternalResource::~ExternalResource() { Q_CLEANUP_RESOURCE(qsynedit_qmake_qmake_qm_files); Q_CLEANUP_RESOURCE(redpanda_qt_utils_qmake_qmake_qm_files); } #ifdef Q_OS_WINDOWS bool applicationHasUtf8Manifest(const wchar_t *path) { auto module = resourcePointer(LoadLibraryExW(path, nullptr, LOAD_LIBRARY_AS_DATAFILE), &FreeLibrary); if (!module) return false; HRSRC resInfo = FindResourceW( module.get(), MAKEINTRESOURCEW(1) /* CREATEPROCESS_MANIFEST_RESOURCE_ID */, MAKEINTRESOURCEW(24) /* RT_MANIFEST */); if (!resInfo) return false; auto res = resourcePointer(LoadResource(module.get(), resInfo), &FreeResource); if (!res) return false; char *data = (char *)LockResource(res.get()); DWORD size = SizeofResource(module.get(), resInfo); QByteArray manifest(data, size); QDomDocument doc; if (!doc.setContent(manifest)) return false; QDomNodeList acpNodes = doc.elementsByTagName("activeCodePage"); if (acpNodes.isEmpty()) return false; QDomElement acpNode = acpNodes.item(0).toElement(); // case sensitive // ref. https://devblogs.microsoft.com/oldnewthing/20220531-00/?p=106697 return acpNode.text() == QStringLiteral("UTF-8"); } bool osSupportsUtf8Manifest() { // since Windows 10 1903 (accurate build number unknown) // ref. https://learn.microsoft.com/en-us/windows/apps/design/globalizing/use-utf8-code-page return QOperatingSystemVersion::current().microVersion() >= 18362; } bool applicationIsUtf8(const QString &path) { return osSupportsUtf8Manifest() && applicationHasUtf8Manifest((wchar_t *)path.constData()); } #endif