KCoreAddons

kosrelease.cpp
1/*
2 SPDX-FileCopyrightText: 2014-2019 Harald Sitter <sitter@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
5*/
6
7#include "kosrelease.h"
8
9#include <QFile>
10
11#include "kcoreaddons_debug.h"
12#include "kshell.h"
13
14// Sets a QString var
15static void setVar(QString *var, const QString &value)
16{
17 // Values may contain quotation marks, strip them as we have no use for them.
19 QStringList args = KShell::splitArgs(value, KShell::NoOptions, &error);
20 if (error != KShell::NoError) { // Failed to parse.
21 return;
22 }
23 *var = args.join(QLatin1Char(' '));
24}
25
26// Sets a QStringList var (i.e. splits a string value)
27static void setVar(QStringList *var, const QString &value)
28{
29 // Instead of passing the verbatim value we manually strip any initial quotes
30 // and then run it through KShell. At this point KShell will actually split
31 // by spaces giving us the final QStringList.
32 // NOTE: Splitting like this does not actually allow escaped substrings to
33 // be handled correctly, so "kitteh \"french fries\"" would result in
34 // three list entries. I'd argue that if someone makes an id like that
35 // they are at fault for the bogus parsing here though as id explicitly
36 // is required to not contain spaces even if more advanced shell escaping
37 // is also allowed...
38 QString value_ = value;
39 if (value_.at(0) == QLatin1Char('"') && value_.at(value_.size() - 1) == QLatin1Char('"')) {
40 value_.remove(0, 1);
41 value_.remove(-1, 1);
42 }
44 QStringList args = KShell::splitArgs(value_, KShell::NoOptions, &error);
45 if (error != KShell::NoError) { // Failed to parse.
46 return;
47 }
48 *var = args;
49}
50
51static QStringList splitEntry(const QString &line)
52{
54 const int separatorIndex = line.indexOf(QLatin1Char('='));
55 list << line.mid(0, separatorIndex);
56 if (separatorIndex != -1) {
57 list << line.mid(separatorIndex + 1, -1);
58 }
59 return list;
60}
61
62static QString defaultFilePath()
63{
64 if (QFile::exists(QStringLiteral("/etc/os-release"))) {
65 return QStringLiteral("/etc/os-release");
66 } else if (QFile::exists(QStringLiteral("/usr/lib/os-release"))) {
67 return QStringLiteral("/usr/lib/os-release");
68 } else {
69 return QString();
70 }
71}
72
73class KOSReleasePrivate
74{
75public:
76 explicit KOSReleasePrivate(QString filePath)
77 : name(QStringLiteral("Linux"))
78 , id(QStringLiteral("linux"))
79 , prettyName(QStringLiteral("Linux"))
80 {
81 // Default values for non-optional fields set above ^.
82
83 QHash<QString, QString *> stringHash = {{QStringLiteral("NAME"), &name},
84 {QStringLiteral("VERSION"), &version},
85 {QStringLiteral("ID"), &id},
86 // idLike is not a QString, special handling below!
87 {QStringLiteral("VERSION_CODENAME"), &versionCodename},
88 {QStringLiteral("VERSION_ID"), &versionId},
89 {QStringLiteral("PRETTY_NAME"), &prettyName},
90 {QStringLiteral("ANSI_COLOR"), &ansiColor},
91 {QStringLiteral("CPE_NAME"), &cpeName},
92 {QStringLiteral("HOME_URL"), &homeUrl},
93 {QStringLiteral("DOCUMENTATION_URL"), &documentationUrl},
94 {QStringLiteral("SUPPORT_URL"), &supportUrl},
95 {QStringLiteral("BUG_REPORT_URL"), &bugReportUrl},
96 {QStringLiteral("PRIVACY_POLICY_URL"), &privacyPolicyUrl},
97 {QStringLiteral("BUILD_ID"), &buildId},
98 {QStringLiteral("VARIANT"), &variant},
99 {QStringLiteral("VARIANT_ID"), &variantId},
100 {QStringLiteral("LOGO"), &logo}};
101
102 if (filePath.isEmpty()) {
103 filePath = defaultFilePath();
104 }
105 if (filePath.isEmpty()) {
106 qCWarning(KCOREADDONS_DEBUG) << "Failed to find os-release file!";
107 return;
108 }
109
110 QFile file(filePath);
111 // NOTE: The os-release specification defines default values for specific
112 // fields which means that even if we can not read the os-release file
113 // we have sort of expected default values to use.
114 // TODO: it might still be handy to indicate to the outside whether
115 // fallback values are being used or not.
116 if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
117 qCWarning(KCOREADDONS_DEBUG) << "Failed to open os-release file!" << file.errorString();
118 return;
119 }
120 QString line;
121 QStringList parts;
122 while (!file.atEnd()) {
123 // Trimmed to handle indented comment lines properly
124 line = QString::fromUtf8(file.readLine()).trimmed();
125
126 if (line.startsWith(QLatin1Char('#'))) {
127 // Comment line
128 // Lines beginning with "#" shall be ignored as comments.
129 continue;
130 }
131
132 parts = splitEntry(line);
133
134 if (parts.size() != 2) {
135 // Line has no =, must be invalid.
136 qCDebug(KCOREADDONS_DEBUG) << "Unexpected/invalid os-release line:" << line;
137 continue;
138 }
139
140 QString key = parts.at(0);
141 QString value = parts.at(1).trimmed();
142
143 if (QString *var = stringHash.value(key, nullptr)) {
144 setVar(var, value);
145 continue;
146 }
147
148 // ID_LIKE is a list and parsed as such (rather than a QString).
149 if (key == QLatin1String("ID_LIKE")) {
150 setVar(&idLike, value);
151 continue;
152 }
153
154 // os-release explicitly allows for vendor specific additions, we'll
155 // collect them as strings and exposes them as "extras".
156 QString parsedValue;
157 setVar(&parsedValue, value);
158 extras.insert(key, parsedValue);
159 }
160 }
161
164 QString id;
165 QStringList idLike;
166 QString versionCodename;
167 QString versionId;
168 QString prettyName;
169 QString ansiColor;
170 QString cpeName;
171 QString homeUrl;
172 QString documentationUrl;
173 QString supportUrl;
174 QString bugReportUrl;
175 QString privacyPolicyUrl;
176 QString buildId;
177 QString variant;
178 QString variantId;
179 QString logo;
180
182};
183
185 : d(new KOSReleasePrivate(filePath))
186{
187}
188
189KOSRelease::~KOSRelease() = default;
190
192{
193 return d->name;
194}
195
197{
198 return d->version;
199}
200
202{
203 return d->id;
204}
205
207{
208 return d->idLike;
209}
210
212{
213 return d->versionCodename;
214}
215
217{
218 return d->versionId;
219}
220
222{
223 return d->prettyName;
224}
225
227{
228 return d->ansiColor;
229}
230
232{
233 return d->cpeName;
234}
235
237{
238 return d->homeUrl;
239}
240
242{
243 return d->documentationUrl;
244}
245
247{
248 return d->supportUrl;
249}
250
252{
253 return d->bugReportUrl;
254}
255
257{
258 return d->privacyPolicyUrl;
259}
260
262{
263 return d->buildId;
264}
265
267{
268 return d->variant;
269}
270
272{
273 return d->variantId;
274}
275
277{
278 return d->logo;
279}
280
282{
283 return d->extras.keys();
284}
285
287{
288 return d->extras.value(key);
289}
QString documentationUrl() const
QString buildId() const
QString version() const
QString extraValue(const QString &key) const
Extra values are values assoicated with keys that are unknown.
QString supportUrl() const
QString bugReportUrl() const
QString versionCodename() const
QString privacyPolicyUrl() const
QString versionId() const
QString cpeName() const
QString ansiColor() const
QString id() const
QStringList idLike() const
QString homeUrl() const
QString prettyName() const
QString logo() const
QString name() const
QStringList extraKeys() const
Extra keys are keys that are unknown or specified by a vendor.
KOSRelease(const QString &filePath=QString())
Constructs a new OSRelease instance.
QString variantId() const
QString variant() const
KDB_EXPORT KDbVersionInfo version()
void error(QWidget *parent, const QString &text, const QString &title, const KGuiItem &buttonOk, Options options=Notify)
KIOCORE_EXPORT QStringList list(const QString &fileClass)
KCOREADDONS_EXPORT QStringList splitArgs(const QString &cmd, Options flags=NoOptions, Errors *err=nullptr)
Splits cmd according to system shell word splitting and quoting rules.
Errors
Status codes from splitArgs()
Definition kshell.h:78
@ NoError
Success.
Definition kshell.h:82
QString name(StandardAction id)
bool exists(const QString &fileName)
T value(const Key &key) const const
const_reference at(qsizetype i) const const
const QChar at(qsizetype position) const const
qsizetype indexOf(QChar ch, qsizetype from, Qt::CaseSensitivity cs) const const
bool isEmpty() const const
QString mid(qsizetype position, qsizetype n) const const
QString & remove(QChar ch, Qt::CaseSensitivity cs)
qsizetype size() const const
QString join(QChar separator) const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2025 The KDE developers.
Generated on Fri Jan 31 2025 11:58:24 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.