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