You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
libcutefish/application/mimeapps.cpp

166 lines
5.5 KiB
C++

// SPDX-FileCopyrightText: 2026 Reion Wong <reionwong@gmail.com>
// SPDX-License-Identifier: GPL-3.0-or-later
#include "mimeapps.h"
#include <QDir>
#include <QFile>
#include <QFileInfo>
#include <QLockFile>
#include <QSaveFile>
#include <QStandardPaths>
namespace {
QStringList readEntries(const QStringList &lines, const QString &group, const QString &key)
{
QString current;
for (const QString &line : lines) {
const QString trimmed = line.trimmed();
if (trimmed.startsWith('[') && trimmed.endsWith(']'))
current = trimmed.mid(1, trimmed.size() - 2);
else if (current == group && !trimmed.startsWith('#')) {
const auto equals = line.indexOf('=');
if (equals >= 0 && line.left(equals).trimmed() == key)
return line.mid(equals + 1).trimmed().split(';', Qt::SkipEmptyParts);
}
}
return {};
}
void writeEntry(QStringList &lines, const QString &group, const QString &key,
const QStringList &values)
{
const QString entry = key + '=' + values.join(';') + ';';
QString current;
int insertion = -1;
for (int i = 0; i < lines.size(); ++i) {
const QString trimmed = lines.at(i).trimmed();
if (trimmed.startsWith('[') && trimmed.endsWith(']')) {
if (current == group)
break;
current = trimmed.mid(1, trimmed.size() - 2);
if (current == group)
insertion = i + 1;
} else if (current == group) {
const auto equals = lines.at(i).indexOf('=');
if (equals >= 0 && lines.at(i).left(equals).trimmed() == key) {
lines.removeAt(i--);
} else {
insertion = i + 1;
}
}
}
if (insertion < 0) {
lines.append('[' + group + ']');
insertion = lines.size();
}
lines.insert(insertion, entry);
}
// An entry may carry a "+" (include) or "-" (exclude) prefix and an ":action" suffix.
QString terminalId(const QString &line)
{
QString entry = line.trimmed();
if (entry.startsWith('+') || entry.startsWith('-'))
entry.remove(0, 1);
return entry.section(':', 0, 0);
}
bool writeLines(const QString &path, const QStringList &lines)
{
QSaveFile output(path);
if (!output.open(QIODevice::WriteOnly))
return false;
const auto data = (lines.join('\n') + '\n').toUtf8();
return output.write(data) == data.size() && output.commit();
}
} // namespace
QString MimeApps::userListPath()
{
return QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation)
+ QLatin1String("/mimeapps.list");
}
QString MimeApps::userTerminalsListPath()
{
return QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation)
+ QLatin1String("/xdg-terminals.list");
}
QStringList MimeApps::associations(const QString &path, const QString &group,
const QString &mimeType)
{
QFile file(path);
if (!file.open(QIODevice::ReadOnly))
return {};
return readEntries(QString::fromUtf8(file.readAll()).split('\n'), group, mimeType);
}
bool MimeApps::setDefault(const QString &path, const QString &mimeType, const QString &desktopId)
{
if (mimeType.isEmpty() || mimeType.contains('\n') || mimeType.contains('\r')
|| mimeType.contains('=') || desktopId.isEmpty() || desktopId.contains('\n')
|| desktopId.contains('\r') || desktopId.contains(';'))
return false;
if (!QDir().mkpath(QFileInfo(path).absolutePath()))
return false;
QLockFile lock(path + ".lock");
if (!lock.tryLock())
return false;
QFile input(path);
QStringList lines;
if (input.exists()) {
if (!input.open(QIODevice::ReadOnly))
return false;
lines = QString::fromUtf8(input.readAll()).split('\n');
input.close();
}
auto added = readEntries(lines, "Added Associations", mimeType);
added.removeAll(desktopId);
added.prepend(desktopId);
writeEntry(lines, "Default Applications", mimeType, {desktopId});
writeEntry(lines, "Added Associations", mimeType, added);
auto removed = readEntries(lines, "Removed Associations", mimeType);
if (removed.removeAll(desktopId))
writeEntry(lines, "Removed Associations", mimeType, removed);
return writeLines(path, lines);
}
QString MimeApps::defaultTerminal(const QString &path)
{
QFile file(path);
if (!file.open(QIODevice::ReadOnly))
return {};
for (const QString &line : QString::fromUtf8(file.readAll()).split('\n')) {
const QString entry = line.trimmed();
if (!entry.isEmpty() && !entry.startsWith('#') && !entry.startsWith('-'))
return terminalId(entry);
}
return {};
}
bool MimeApps::setDefaultTerminal(const QString &path, const QString &desktopId)
{
if (desktopId.isEmpty() || desktopId.contains('\n') || desktopId.contains('\r'))
return false;
if (!QDir().mkpath(QFileInfo(path).absolutePath()))
return false;
QLockFile lock(path + ".lock");
if (!lock.tryLock())
return false;
QFile input(path);
QStringList lines;
if (input.exists()) {
if (!input.open(QIODevice::ReadOnly))
return false;
lines = QString::fromUtf8(input.readAll()).split('\n', Qt::SkipEmptyParts);
input.close();
}
lines.removeIf([&desktopId](const QString &line) {
return !line.trimmed().startsWith('#') && terminalId(line) == desktopId;
});
lines.prepend(desktopId);
return writeLines(path, lines);
}