1 | // Copyright (C) 2016 Sune Vuorela <sune@kde.org> |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause |
3 | |
4 | #include <QCoreApplication> |
5 | #include <QCommandLineParser> |
6 | #include <QStandardPaths> |
7 | #include <QHash> |
8 | #include <QLibraryInfo> |
9 | |
10 | #include <algorithm> |
11 | |
12 | #include <stdio.h> |
13 | |
14 | #if QT_CONFIG(settings) |
15 | # include <private/qlibraryinfo_p.h> |
16 | # include <qmakelibraryinfo.h> |
17 | # include <propertyprinter.h> |
18 | # include <property.h> |
19 | #endif |
20 | |
21 | QT_USE_NAMESPACE |
22 | |
23 | /** |
24 | * Prints the string on stdout and appends a newline |
25 | * \param string printable string |
26 | */ |
27 | static void message(const QString &string) |
28 | { |
29 | fprintf(stdout, format: "%s\n" , qPrintable(string)); |
30 | } |
31 | |
32 | /** |
33 | * Writes error message and exits 1 |
34 | * \param message to write |
35 | */ |
36 | Q_NORETURN static void error(const QString &message) |
37 | { |
38 | fprintf(stderr, format: "%s\n" , qPrintable(message)); |
39 | ::exit(EXIT_FAILURE); |
40 | } |
41 | |
42 | class StringEnum { |
43 | public: |
44 | const char *stringvalue; |
45 | QStandardPaths::StandardLocation enumvalue; |
46 | bool hasappname; |
47 | |
48 | /** |
49 | * Replace application name by generic name if requested |
50 | */ |
51 | QString mapName(const QString &s) const |
52 | { |
53 | return hasappname ? QString(s).replace(before: "qtpaths" , after: "<APPNAME>" ) : s; |
54 | } |
55 | }; |
56 | |
57 | static const StringEnum lookupTableData[] = { |
58 | { .stringvalue: "AppConfigLocation" , .enumvalue: QStandardPaths::AppConfigLocation, .hasappname: true }, |
59 | { .stringvalue: "AppDataLocation" , .enumvalue: QStandardPaths::AppDataLocation, .hasappname: true }, |
60 | { .stringvalue: "AppLocalDataLocation" , .enumvalue: QStandardPaths::AppLocalDataLocation, .hasappname: true }, |
61 | { .stringvalue: "ApplicationsLocation" , .enumvalue: QStandardPaths::ApplicationsLocation, .hasappname: false }, |
62 | { .stringvalue: "CacheLocation" , .enumvalue: QStandardPaths::CacheLocation, .hasappname: true }, |
63 | { .stringvalue: "ConfigLocation" , .enumvalue: QStandardPaths::ConfigLocation, .hasappname: false }, |
64 | #if QT_VERSION < QT_VERSION_CHECK(6,0,0) |
65 | { "DataLocation" , QStandardPaths::DataLocation, true }, |
66 | #endif |
67 | { .stringvalue: "DesktopLocation" , .enumvalue: QStandardPaths::DesktopLocation, .hasappname: false }, |
68 | { .stringvalue: "DocumentsLocation" , .enumvalue: QStandardPaths::DocumentsLocation, .hasappname: false }, |
69 | { .stringvalue: "DownloadLocation" , .enumvalue: QStandardPaths::DownloadLocation, .hasappname: false }, |
70 | { .stringvalue: "FontsLocation" , .enumvalue: QStandardPaths::FontsLocation, .hasappname: false }, |
71 | { .stringvalue: "GenericCacheLocation" , .enumvalue: QStandardPaths::GenericCacheLocation, .hasappname: false }, |
72 | { .stringvalue: "GenericConfigLocation" , .enumvalue: QStandardPaths::GenericConfigLocation, .hasappname: false }, |
73 | { .stringvalue: "GenericDataLocation" , .enumvalue: QStandardPaths::GenericDataLocation, .hasappname: false }, |
74 | { .stringvalue: "HomeLocation" , .enumvalue: QStandardPaths::HomeLocation, .hasappname: false }, |
75 | { .stringvalue: "MoviesLocation" , .enumvalue: QStandardPaths::MoviesLocation, .hasappname: false }, |
76 | { .stringvalue: "MusicLocation" , .enumvalue: QStandardPaths::MusicLocation, .hasappname: false }, |
77 | { .stringvalue: "PicturesLocation" , .enumvalue: QStandardPaths::PicturesLocation, .hasappname: false }, |
78 | { .stringvalue: "PublicShareLocation" , .enumvalue: QStandardPaths::PublicShareLocation, .hasappname: false }, |
79 | { .stringvalue: "RuntimeLocation" , .enumvalue: QStandardPaths::RuntimeLocation, .hasappname: false }, |
80 | { .stringvalue: "TemplatesLocation" , .enumvalue: QStandardPaths::TemplatesLocation, .hasappname: false }, |
81 | { .stringvalue: "TempLocation" , .enumvalue: QStandardPaths::TempLocation, .hasappname: false } |
82 | }; |
83 | |
84 | /** |
85 | * \return available types as a QStringList. |
86 | */ |
87 | static QStringList types() |
88 | { |
89 | QStringList typelist; |
90 | for (const StringEnum &se : lookupTableData) |
91 | typelist << QString::fromLatin1(ba: se.stringvalue); |
92 | std::sort(first: typelist.begin(), last: typelist.end()); |
93 | return typelist; |
94 | } |
95 | |
96 | /** |
97 | * Tries to parse the location string into a reference to a StringEnum entry or alternatively |
98 | * calls \ref error with a error message |
99 | */ |
100 | static const StringEnum &parseLocationOrError(const QString &locationString) |
101 | { |
102 | for (const StringEnum &se : lookupTableData) |
103 | if (locationString == QLatin1StringView(se.stringvalue)) |
104 | return se; |
105 | |
106 | QString message = QStringLiteral("Unknown location: %1" ); |
107 | error(message: message.arg(a: locationString)); |
108 | } |
109 | |
110 | /** |
111 | * searches for exactly one remaining argument and returns it. |
112 | * If not found, \ref error is called with a error message. |
113 | * \param parser to ask for remaining arguments |
114 | * \return one extra argument |
115 | */ |
116 | static QString searchStringOrError(QCommandLineParser *parser) |
117 | { |
118 | int positionalArgumentCount = parser->positionalArguments().size(); |
119 | if (positionalArgumentCount != 1) |
120 | error(QStringLiteral("Exactly one argument needed as searchitem" )); |
121 | return parser->positionalArguments().constFirst(); |
122 | } |
123 | |
124 | int main(int argc, char **argv) |
125 | { |
126 | QString qtconfManualPath; |
127 | QCoreApplication app(argc, argv); |
128 | app.setApplicationVersion(QTPATHS_VERSION_STR); |
129 | |
130 | #ifdef Q_OS_WIN |
131 | const QLatin1Char pathsep(';'); |
132 | #else |
133 | const QLatin1Char pathsep(':'); |
134 | #endif |
135 | |
136 | QCommandLineParser parser; |
137 | parser.setApplicationDescription(QStringLiteral("Command line client to QStandardPaths and QLibraryInfo" )); |
138 | parser.addPositionalArgument(QStringLiteral("[name]" ), QStringLiteral("Name of file or directory" )); |
139 | parser.addPositionalArgument(QStringLiteral("[properties]" ), QStringLiteral("List of the Qt properties to query by the --qt-query argument." )); |
140 | parser.setSingleDashWordOptionMode(QCommandLineParser::ParseAsLongOptions); |
141 | parser.addHelpOption(); |
142 | parser.addVersionOption(); |
143 | |
144 | //setting up options |
145 | QCommandLineOption types(QStringLiteral("types" ), QStringLiteral("Available location types." )); |
146 | parser.addOption(commandLineOption: types); |
147 | |
148 | QCommandLineOption paths(QStringLiteral("paths" ), QStringLiteral("Find paths for <type>." ), QStringLiteral("type" )); |
149 | parser.addOption(commandLineOption: paths); |
150 | |
151 | QCommandLineOption writablePath(QStringLiteral("writable-path" ), |
152 | QStringLiteral("Find writable path for <type>." ), QStringLiteral("type" )); |
153 | parser.addOption(commandLineOption: writablePath); |
154 | |
155 | QCommandLineOption locateDir(QStringList() << QStringLiteral("locate-dir" ) << QStringLiteral("locate-directory" ), |
156 | QStringLiteral("Locate directory [name] in <type>." ), QStringLiteral("type" )); |
157 | parser.addOption(commandLineOption: locateDir); |
158 | |
159 | QCommandLineOption locateDirs(QStringList() << QStringLiteral("locate-dirs" ) << QStringLiteral("locate-directories" ), |
160 | QStringLiteral("Locate directories [name] in all paths for <type>." ), QStringLiteral("type" )); |
161 | parser.addOption(commandLineOption: locateDirs); |
162 | |
163 | QCommandLineOption locateFile(QStringLiteral("locate-file" ), |
164 | QStringLiteral("Locate file [name] for <type>." ), QStringLiteral("type" )); |
165 | parser.addOption(commandLineOption: locateFile); |
166 | |
167 | QCommandLineOption locateFiles(QStringLiteral("locate-files" ), |
168 | QStringLiteral("Locate files [name] in all paths for <type>." ), QStringLiteral("type" )); |
169 | parser.addOption(commandLineOption: locateFiles); |
170 | |
171 | QCommandLineOption findExe(QStringList() << QStringLiteral("find-exe" ) << QStringLiteral("find-executable" ), |
172 | QStringLiteral("Find executable with [name]." )); |
173 | parser.addOption(commandLineOption: findExe); |
174 | |
175 | QCommandLineOption display(QStringList() << QStringLiteral("display" ), |
176 | QStringLiteral("Prints user readable name for <type>." ), QStringLiteral("type" )); |
177 | parser.addOption(commandLineOption: display); |
178 | |
179 | QCommandLineOption testmode(QStringList() << QStringLiteral("testmode" ) << QStringLiteral("test-mode" ), |
180 | QStringLiteral("Use paths specific for unit testing." )); |
181 | parser.addOption(commandLineOption: testmode); |
182 | |
183 | QCommandLineOption qtversion(QStringLiteral("qt-version" ), QStringLiteral("Qt version." )); |
184 | qtversion.setFlags(QCommandLineOption::HiddenFromHelp); |
185 | parser.addOption(commandLineOption: qtversion); |
186 | |
187 | QCommandLineOption installprefix(QStringLiteral("install-prefix" ), QStringLiteral("Installation prefix for Qt." )); |
188 | installprefix.setFlags(QCommandLineOption::HiddenFromHelp); |
189 | parser.addOption(commandLineOption: installprefix); |
190 | |
191 | QCommandLineOption bindir(QStringList() << QStringLiteral("binaries-dir" ) << QStringLiteral("binaries-directory" ), |
192 | QStringLiteral("Location of Qt executables." )); |
193 | bindir.setFlags(QCommandLineOption::HiddenFromHelp); |
194 | parser.addOption(commandLineOption: bindir); |
195 | |
196 | QCommandLineOption plugindir(QStringList() << QStringLiteral("plugin-dir" ) << QStringLiteral("plugin-directory" ), |
197 | QStringLiteral("Location of Qt plugins." )); |
198 | plugindir.setFlags(QCommandLineOption::HiddenFromHelp); |
199 | parser.addOption(commandLineOption: plugindir); |
200 | |
201 | QCommandLineOption query( |
202 | QStringList() << QStringLiteral("qt-query" ) << QStringLiteral("query" ), |
203 | QStringLiteral("List of Qt properties. Can be used standalone or with the " |
204 | "--query-format and --qtconf options." )); |
205 | parser.addOption(commandLineOption: query); |
206 | |
207 | QCommandLineOption queryformat(QStringLiteral("query-format" ), |
208 | QStringLiteral("Output format for --qt-query.\nSupported formats: qmake (default), json" ), |
209 | QStringLiteral("format" )); |
210 | queryformat.setDefaultValue("qmake" ); |
211 | parser.addOption(commandLineOption: queryformat); |
212 | |
213 | QCommandLineOption qtconf(QStringLiteral("qtconf" ), |
214 | QStringLiteral("Path to qt.conf file that will be used to override the queried Qt properties." ), |
215 | QStringLiteral("path" )); |
216 | parser.addOption(commandLineOption: qtconf); |
217 | |
218 | parser.process(app); |
219 | |
220 | QStandardPaths::setTestModeEnabled(parser.isSet(option: testmode)); |
221 | |
222 | #if QT_CONFIG(settings) |
223 | if (parser.isSet(option: qtconf)) { |
224 | qtconfManualPath = parser.value(option: qtconf); |
225 | QLibraryInfoPrivate::qtconfManualPath = &qtconfManualPath; |
226 | } |
227 | #endif |
228 | |
229 | QStringList results; |
230 | if (parser.isSet(option: qtversion)) { |
231 | QString qtversionstring = QString::fromLatin1(QT_VERSION_STR); |
232 | results << qtversionstring; |
233 | } |
234 | |
235 | if (parser.isSet(option: installprefix)) { |
236 | QString path = QLibraryInfo::path(p: QLibraryInfo::PrefixPath); |
237 | results << path; |
238 | } |
239 | |
240 | if (parser.isSet(option: bindir)) { |
241 | QString path = QLibraryInfo::path(p: QLibraryInfo::BinariesPath); |
242 | results << path; |
243 | } |
244 | |
245 | if (parser.isSet(option: plugindir)) { |
246 | QString path = QLibraryInfo::path(p: QLibraryInfo::PluginsPath); |
247 | results << path; |
248 | } |
249 | |
250 | if (parser.isSet(option: types)) { |
251 | QStringList typesList = ::types(); |
252 | results << typesList.join(sep: '\n'); |
253 | } |
254 | |
255 | QT_WARNING_PUSH |
256 | #if defined(Q_CC_GNU_ONLY) && Q_CC_GNU >= 1300 && Q_CC_GNU < 1400 |
257 | QT_WARNING_DISABLE_GCC("-Wdangling-reference" ) |
258 | #endif |
259 | if (parser.isSet(option: display)) { |
260 | const StringEnum &location = parseLocationOrError(locationString: parser.value(option: display)); |
261 | QString text = QStandardPaths::displayName(type: location.enumvalue); |
262 | results << location.mapName(s: text); |
263 | } |
264 | |
265 | if (parser.isSet(option: paths)) { |
266 | const StringEnum &location = parseLocationOrError(locationString: parser.value(option: paths)); |
267 | QStringList paths = QStandardPaths::standardLocations(type: location.enumvalue); |
268 | results << location.mapName(s: paths.join(sep: pathsep)); |
269 | } |
270 | |
271 | if (parser.isSet(option: writablePath)) { |
272 | const StringEnum &location = parseLocationOrError(locationString: parser.value(option: writablePath)); |
273 | QString path = QStandardPaths::writableLocation(type: location.enumvalue); |
274 | results << location.mapName(s: path); |
275 | } |
276 | |
277 | if (parser.isSet(option: findExe)) { |
278 | QString searchitem = searchStringOrError(parser: &parser); |
279 | QString path = QStandardPaths::findExecutable(executableName: searchitem); |
280 | results << path; |
281 | } |
282 | |
283 | if (parser.isSet(option: locateDir)) { |
284 | const StringEnum &location = parseLocationOrError(locationString: parser.value(option: locateDir)); |
285 | QString searchitem = searchStringOrError(parser: &parser); |
286 | QString path = QStandardPaths::locate(type: location.enumvalue, fileName: searchitem, options: QStandardPaths::LocateDirectory); |
287 | results << location.mapName(s: path); |
288 | } |
289 | |
290 | if (parser.isSet(option: locateFile)) { |
291 | const StringEnum &location = parseLocationOrError(locationString: parser.value(option: locateFile)); |
292 | QString searchitem = searchStringOrError(parser: &parser); |
293 | QString path = QStandardPaths::locate(type: location.enumvalue, fileName: searchitem, options: QStandardPaths::LocateFile); |
294 | results << location.mapName(s: path); |
295 | } |
296 | |
297 | if (parser.isSet(option: locateDirs)) { |
298 | const StringEnum &location = parseLocationOrError(locationString: parser.value(option: locateDirs)); |
299 | QString searchitem = searchStringOrError(parser: &parser); |
300 | QStringList paths = QStandardPaths::locateAll(type: location.enumvalue, fileName: searchitem, options: QStandardPaths::LocateDirectory); |
301 | results << location.mapName(s: paths.join(sep: pathsep)); |
302 | } |
303 | |
304 | if (parser.isSet(option: locateFiles)) { |
305 | const StringEnum &location = parseLocationOrError(locationString: parser.value(option: locateFiles)); |
306 | QString searchitem = searchStringOrError(parser: &parser); |
307 | QStringList paths = QStandardPaths::locateAll(type: location.enumvalue, fileName: searchitem, options: QStandardPaths::LocateFile); |
308 | results << location.mapName(s: paths.join(sep: pathsep)); |
309 | } |
310 | QT_WARNING_POP |
311 | |
312 | #if !QT_CONFIG(settings) |
313 | if (parser.isSet(query) || parser.isSet(qtconf) || parser.isSet(queryformat)) { |
314 | error(QStringLiteral("--qt-query, --qtconf and --query-format options are not supported. The 'settings' feature is missing." )); |
315 | } |
316 | #else |
317 | if (parser.isSet(option: query)) { |
318 | if (!results.isEmpty()) { |
319 | QString errorMessage = QStringLiteral("Several options given, only one is supported at a time." ); |
320 | error(message: errorMessage); |
321 | } |
322 | |
323 | PropertyPrinter printer; |
324 | if (parser.isSet(option: queryformat)) { |
325 | QString formatValue = parser.value(option: queryformat); |
326 | if (formatValue == "json" ) { |
327 | printer = jsonPropertyPrinter; |
328 | } else if (formatValue != "qmake" ) { |
329 | QString errorMessage = QStringLiteral("Invalid output format %1. Supported formats: qmake, json" ).arg(a: formatValue); |
330 | error(message: errorMessage); |
331 | } |
332 | } |
333 | |
334 | QStringList optionProperties = parser.positionalArguments(); |
335 | QMakeProperty prop; |
336 | if (printer) { |
337 | return prop.queryProperty(optionProperties, printer); |
338 | } |
339 | return prop.queryProperty(optionProperties); |
340 | } else if (parser.isSet(option: queryformat)) { |
341 | error(QStringLiteral("--query-format is set, but --qt-query is not requested." )); |
342 | } |
343 | #endif |
344 | |
345 | if (results.isEmpty()) { |
346 | parser.showHelp(); |
347 | } else if (results.size() == 1) { |
348 | const QString &item = results.first(); |
349 | message(string: item); |
350 | if (item.isEmpty()) |
351 | return EXIT_FAILURE; |
352 | } else { |
353 | QString errorMessage = QStringLiteral("Several options given, only one is supported at a time." ); |
354 | error(message: errorMessage); |
355 | } |
356 | return EXIT_SUCCESS; |
357 | } |
358 | |