1 /*
2 SPDX-FileCopyrightText: 2016 Anton Anikin <anton.anikin@htower.ru>
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
7 #include "kdevformatfile.h"
8 #include "wildcardhelpers.h"
9
10 #include <QDir>
11 #include <QFile>
12 #include <QFileInfo>
13 #include <QProcess>
14
15 #include <utility>
16
17 namespace KDevelop {
18
KDevFormatFile(const QString & origFilePath,const QString & tempFilePath)19 KDevFormatFile::KDevFormatFile(const QString& origFilePath, const QString& tempFilePath)
20 : formatFileName{QStringLiteral("format_sources")}
21 , m_origFilePath(origFilePath)
22 , m_tempFilePath(tempFilePath)
23 {
24 }
25
find()26 bool KDevFormatFile::find()
27 {
28 QDir srcDir(QFileInfo(m_origFilePath).canonicalPath());
29
30 do {
31 if (srcDir.exists(formatFileName)) {
32 QDir::setCurrent(srcDir.canonicalPath());
33
34 qStdOut() << "found \""
35 << QFileInfo(srcDir.canonicalPath() + QDir::separator() + formatFileName).canonicalFilePath()
36 << "\"\n";
37 return true;
38 }
39 } while (!srcDir.isRoot() && srcDir.cdUp());
40
41 return false;
42 }
43
read()44 bool KDevFormatFile::read()
45 {
46 constexpr QChar delimiter = QLatin1Char(':');
47
48 QFile formatFile(formatFileName);
49 if (!formatFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
50 qStdOut() << "unable to open \"" << formatFileName << "\"\n";
51 return false;
52 }
53
54 int lineNumber = 0;
55 while (!formatFile.atEnd()) {
56 ++lineNumber;
57
58 QString line = QString::fromUtf8(formatFile.readLine().trimmed());
59 if (line.isEmpty() || line.startsWith(QLatin1Char('#')))
60 continue;
61
62 if (line.indexOf(delimiter) < 0) {
63 // We found the simple syntax without wildcards, and only with the command
64 m_formatLines.append({QStringList{}, std::move(line)});
65 } else {
66 // We found the correct syntax with "wildcards : command"
67
68 #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)
69 QStringList wildcards = line.section(delimiter, 0, 0).split(QLatin1Char(' '), Qt::SkipEmptyParts);
70 #else
71 QStringList wildcards = line.section(delimiter, 0, 0).split(QLatin1Char(' '), QString::SkipEmptyParts);
72 #endif
73 QString command = line.section(delimiter, 1).trimmed();
74
75 if (wildcards.isEmpty()) {
76 qStdOut() << formatFileName << ":" << lineNumber
77 << ": error: empty wildcard, skip the line\n";
78 continue;
79 }
80 m_formatLines.append({std::move(wildcards), std::move(command)});
81 }
82 }
83
84 if (m_formatLines.isEmpty()) {
85 qStdOut() << formatFileName << ": error: no commands are found\n";
86 return false;
87 }
88
89 return true;
90 }
91
apply()92 bool KDevFormatFile::apply()
93 {
94 for (const KDevFormatLine& formatLine : qAsConst(m_formatLines)) {
95 if (formatLine.wildcards.isEmpty()) {
96 qStdOut() << "matched \"" << m_origFilePath << "\" without wildcard";
97 return executeCommand(formatLine.command);
98 }
99
100 const QChar dirSeparator = QDir::separator();
101 for (const QString& wildcard : formatLine.wildcards) {
102 const QString pattern = QDir::current().canonicalPath() + dirSeparator + wildcard.trimmed();
103 if (WildcardHelpers::matchSinglePattern(pattern, m_origFilePath)) {
104 qStdOut() << "matched \"" << m_origFilePath << "\" with wildcard \"" << wildcard << '\"';
105 return executeCommand(formatLine.command);
106 }
107 }
108 }
109
110 qStdOut() << formatFileName << ": error: no commands applicable to \"" << m_origFilePath << "\"\n";
111 return false;
112 }
113
executeCommand(QString command)114 bool KDevFormatFile::executeCommand(QString command)
115 {
116 if (command.isEmpty()) {
117 qStdOut() << ", empty command => nothing to do\n";
118 return true;
119 }
120 qStdOut() << ", using command \"" << command << "\"\n";
121
122 command.replace(QLatin1String("$ORIGFILE"), m_origFilePath);
123 command.replace(QLatin1String("$TMPFILE"), m_tempFilePath);
124
125 #ifdef Q_OS_WIN
126 const QString interpreter = QStringLiteral("cmd");
127 const QStringList arguments{QStringLiteral("/c"), command};
128 #else
129 const QString interpreter = QStringLiteral("sh");
130 const QStringList arguments{QStringLiteral("-c"), command};
131 #endif
132 const int execResult = QProcess::execute(interpreter, arguments);
133
134 if (execResult != 0) {
135 const QString interpreterDescription = QLatin1String("interpreter ") + interpreter;
136 if (execResult == -2) {
137 qStdOut() << "error: " << interpreterDescription << " failed to start\n";
138 return false;
139 }
140 if (execResult == -1) {
141 qStdOut() << "error: " << interpreterDescription << " crashed\n";
142 return false;
143 }
144 qStdOut() << "warning: " << interpreterDescription << " exited with code " << execResult << '\n';
145 }
146
147 return true;
148 }
149
150 }
151