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
21QT_USE_NAMESPACE
22
23/**
24 * Prints the string on stdout and appends a newline
25 * \param string printable string
26 */
27static 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 */
36Q_NORETURN static void error(const QString &message)
37{
38 fprintf(stderr, format: "%s\n", qPrintable(message));
39 ::exit(EXIT_FAILURE);
40}
41
42class StringEnum {
43public:
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
57static 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 */
87static 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 */
100static 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 */
116static 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
124int 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

source code of qtbase/src/tools/qtpaths/qtpaths.cpp