summaryrefslogtreecommitdiff
path: root/src/plugins/terminal/shellmodel.cpp
blob: b4cf53a1e991ae2042cf09291f787e5222c3a505 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0

#include "shellmodel.h"

#include <utils/algorithm.h>
#include <utils/environment.h>
#include <utils/filepath.h>

#include <QFileIconProvider>
#include <QStandardPaths>

namespace Terminal::Internal {

using namespace Utils;

FilePaths availableShells()
{
    if (Utils::HostOsInfo::isWindowsHost()) {
        FilePaths shells;

        FilePath comspec = FilePath::fromUserInput(qtcEnvironmentVariable("COMSPEC"));
        shells << comspec;

        if (comspec.fileName() != "cmd.exe") {
            FilePath cmd = FilePath::fromUserInput(QStandardPaths::findExecutable("cmd.exe"));
            shells << cmd;
        }

        FilePath powershell = FilePath::fromUserInput(
            QStandardPaths::findExecutable("powershell.exe"));
        if (powershell.exists())
            shells << powershell;

        FilePath bash = FilePath::fromUserInput(QStandardPaths::findExecutable("bash.exe"));
        if (bash.exists())
            shells << bash;

        FilePath git_bash = FilePath::fromUserInput(QStandardPaths::findExecutable("git.exe"));
        if (git_bash.exists())
            shells << git_bash.parentDir().parentDir().pathAppended("usr/bin/bash.exe");

        FilePath msys2_bash = FilePath::fromUserInput(QStandardPaths::findExecutable("msys2.exe"));
        if (msys2_bash.exists())
            shells << msys2_bash.parentDir().pathAppended("usr/bin/bash.exe");

        return shells;
    } else {
        FilePath shellsFile = FilePath::fromString("/etc/shells");
        const auto shellFileContent = shellsFile.fileContents();
        QTC_ASSERT_EXPECTED(shellFileContent, return {});

        QString shellFileContentString = QString::fromUtf8(*shellFileContent);

        // Filter out comments ...
        const QStringList lines
            = Utils::filtered(shellFileContentString.split('\n', Qt::SkipEmptyParts),
                              [](const QString &line) { return !line.trimmed().startsWith('#'); });

        // Convert lines to file paths ...
        const FilePaths shells = Utils::transform(lines, [](const QString &line) {
            return FilePath::fromUserInput(line.trimmed());
        });

        // ... and filter out non-existing shells.
        return Utils::filtered(shells, [](const FilePath &shell) { return shell.exists(); });
    }
}

struct ShellModelPrivate
{
    QList<ShellModelItem> localShells;
};

ShellModel::ShellModel(QObject *parent)
    : QObject(parent)
    , d(new ShellModelPrivate())
{
    QFileIconProvider iconProvider;

    const FilePaths shells = availableShells();
    for (const FilePath &shell : shells) {
        ShellModelItem item;
        item.icon = iconProvider.icon(shell.toFileInfo());
        item.name = shell.toUserOutput();
        item.openParameters.shellCommand = {shell, {}};
        d->localShells << item;
    }
}

ShellModel::~ShellModel() = default;

QList<ShellModelItem> ShellModel::local() const
{
    return d->localShells;
}

QList<ShellModelItem> ShellModel::remote() const
{
    const auto deviceCmds = Utils::Terminal::Hooks::instance().getTerminalCommandsForDevicesHook()();

    const QList<ShellModelItem> deviceItems = Utils::transform(
        deviceCmds, [](const Utils::Terminal::NameAndCommandLine &item) -> ShellModelItem {
            return ShellModelItem{item.name, {}, {item.commandLine, std::nullopt, std::nullopt}};
        });

    return deviceItems;
}

} // namespace Terminal::Internal