KIO

kshellcompletion.cpp
1/*
2 This file is part of the KDE libraries
3 SPDX-FileCopyrightText: 2000 David Smith <dsmith@algonet.se>
4
5 SPDX-License-Identifier: LGPL-2.0-or-later
6*/
7
8#include "kshellcompletion.h"
9
10#include <KCompletion>
11#include <KCompletionMatches>
12#include <stdlib.h>
13
14class KShellCompletionPrivate
15{
16public:
17 KShellCompletionPrivate()
18 : m_word_break_char(QLatin1Char(' '))
19 , m_quote_char1(QLatin1Char('\"'))
20 , m_quote_char2(QLatin1Char('\''))
21 , m_escape_char(QLatin1Char('\\'))
22 {
23 }
24
25 void splitText(const QString &text, QString &text_start, QString &text_compl) const;
26 bool quoteText(QString *text, bool force, bool skip_last) const;
27 QString unquote(const QString &text) const;
28
29 QString m_text_start; // part of the text that was not completed
30 QString m_text_compl; // part of the text that was completed (unchanged)
31
32 QChar m_word_break_char;
33 QChar m_quote_char1;
34 QChar m_quote_char2;
35 QChar m_escape_char;
36};
37
40 , d(new KShellCompletionPrivate)
41{
42}
43
44KShellCompletion::~KShellCompletion() = default;
45
46/*
47 * makeCompletion()
48 *
49 * Entry point for file name completion
50 */
52{
53 // Split text at the last unquoted space
54 //
55 d->splitText(text, d->m_text_start, d->m_text_compl);
56
57 // Remove quotes from the text to be completed
58 //
59 QString tmp = d->unquote(d->m_text_compl);
60 d->m_text_compl = tmp;
61
62 // Do exe-completion if there was no unquoted space
63 //
64 const bool is_exe_completion = !d->m_text_start.contains(d->m_word_break_char);
65
66 setMode(is_exe_completion ? ExeCompletion : FileCompletion);
67
68 // Make completion on the last part of text
69 //
70 return KUrlCompletion::makeCompletion(d->m_text_compl);
71}
72
73/*
74 * postProcessMatch, postProcessMatches
75 *
76 * Called by KCompletion before emitting match() and matches()
77 *
78 * Add add the part of the text that was not completed
79 * Add quotes when needed
80 */
81void KShellCompletion::postProcessMatch(QString *match) const
82{
83 KUrlCompletion::postProcessMatch(match);
84
85 if (match->isNull()) {
86 return;
87 }
88
89 if (match->endsWith(QLatin1Char('/'))) {
90 d->quoteText(match, false, true); // don't quote the trailing '/'
91 } else {
92 d->quoteText(match, false, false); // quote the whole text
93 }
94
95 match->prepend(d->m_text_start);
96}
97
98void KShellCompletion::postProcessMatches(QStringList *matches) const
99{
100 KUrlCompletion::postProcessMatches(matches);
101
102 for (QString &match : *matches) {
103 if (!match.isNull()) {
104 if (match.endsWith(QLatin1Char('/'))) {
105 d->quoteText(&match, false, true); // don't quote trailing '/'
106 } else {
107 d->quoteText(&match, false, false); // quote the whole text
108 }
109
110 match.prepend(d->m_text_start);
111 }
112 }
113}
114
115void KShellCompletion::postProcessMatches(KCompletionMatches *matches) const
116{
117 KUrlCompletion::postProcessMatches(matches);
118
119 for (auto &match : *matches) {
120 QString &matchString = match.value();
121 if (!matchString.isNull()) {
122 if (matchString.endsWith(QLatin1Char('/'))) {
123 d->quoteText(&matchString, false, true); // don't quote trailing '/'
124 } else {
125 d->quoteText(&matchString, false, false); // quote the whole text
126 }
127
128 matchString.prepend(d->m_text_start);
129 }
130 }
131}
132
133/*
134 * splitText
135 *
136 * Split text at the last unquoted space
137 *
138 * text_start = [out] text at the left, including the space
139 * text_compl = [out] text at the right
140 */
141void KShellCompletionPrivate::splitText(const QString &text, QString &text_start, QString &text_compl) const
142{
143 bool in_quote = false;
144 bool escaped = false;
145 QChar p_last_quote_char;
146 int last_unquoted_space = -1;
147
148 for (int pos = 0; pos < text.length(); pos++) {
149 if (escaped) {
150 escaped = false;
151 } else if (in_quote && text[pos] == p_last_quote_char) {
152 in_quote = false;
153 } else if (!in_quote && text[pos] == m_quote_char1) {
154 p_last_quote_char = m_quote_char1;
155 in_quote = true;
156 } else if (!in_quote && text[pos] == m_quote_char2) {
157 p_last_quote_char = m_quote_char2;
158 in_quote = true;
159 } else if (text[pos] == m_escape_char) {
160 escaped = true;
161 } else if (!in_quote && text[pos] == m_word_break_char) {
162 while (pos + 1 < text.length() && text[pos + 1] == m_word_break_char) {
163 pos++;
164 }
165
166 if (pos + 1 == text.length()) {
167 break;
168 }
169
170 last_unquoted_space = pos;
171 }
172 }
173
174 text_start = text.left(last_unquoted_space + 1);
175
176 // the last part without trailing blanks
177 text_compl = text.mid(last_unquoted_space + 1);
178}
179
180/*
181 * quoteText()
182 *
183 * Add quotations to 'text' if needed or if 'force' = true
184 * Returns true if quotes were added
185 *
186 * skip_last => ignore the last character (we add a space or '/' to all filenames)
187 */
188bool KShellCompletionPrivate::quoteText(QString *text, bool force, bool skip_last) const
189{
190 int pos = 0;
191
192 if (!force) {
193 pos = text->indexOf(m_word_break_char);
194 if (skip_last && (pos == (int)(text->length()) - 1)) {
195 pos = -1;
196 }
197 }
198
199 if (!force && pos == -1) {
200 pos = text->indexOf(m_quote_char1);
201 if (skip_last && (pos == (int)(text->length()) - 1)) {
202 pos = -1;
203 }
204 }
205
206 if (!force && pos == -1) {
207 pos = text->indexOf(m_quote_char2);
208 if (skip_last && (pos == (int)(text->length()) - 1)) {
209 pos = -1;
210 }
211 }
212
213 if (!force && pos == -1) {
214 pos = text->indexOf(m_escape_char);
215 if (skip_last && (pos == (int)(text->length()) - 1)) {
216 pos = -1;
217 }
218 }
219
220 if (force || (pos >= 0)) {
221 // Escape \ in the string
222 text->replace(m_escape_char, QString(m_escape_char) + m_escape_char);
223
224 // Escape " in the string
225 text->replace(m_quote_char1, QString(m_escape_char) + m_quote_char1);
226
227 // " at the beginning
228 text->insert(0, m_quote_char1);
229
230 // " at the end
231 if (skip_last) {
232 text->insert(text->length() - 1, m_quote_char1);
233 } else {
234 text->insert(text->length(), m_quote_char1);
235 }
236
237 return true;
238 }
239
240 return false;
241}
242
243/*
244 * unquote
245 *
246 * Remove quotes and return the result in a new string
247 *
248 */
249QString KShellCompletionPrivate::unquote(const QString &text) const
250{
251 bool in_quote = false;
252 bool escaped = false;
253 QChar p_last_quote_char;
254 QString result;
255
256 for (const QChar ch : text) {
257 if (escaped) {
258 escaped = false;
259 result.insert(result.length(), ch);
260 } else if (in_quote && ch == p_last_quote_char) {
261 in_quote = false;
262 } else if (!in_quote && ch == m_quote_char1) {
263 p_last_quote_char = m_quote_char1;
264 in_quote = true;
265 } else if (!in_quote && ch == m_quote_char2) {
266 p_last_quote_char = m_quote_char2;
267 in_quote = true;
268 } else if (ch == m_escape_char) {
269 escaped = true;
270 result.insert(result.length(), ch);
271 } else {
272 result.insert(result.length(), ch);
273 }
274 }
275
276 return result;
277}
278
279#include "moc_kshellcompletion.cpp"
void matches(const QStringList &matchlist)
void match(const QString &item)
QString makeCompletion(const QString &text) override
Finds completions to the given text.
KShellCompletion()
Constructs a KShellCompletion object.
This class does completion of URLs including user directories (~user) and environment variables.
QString makeCompletion(const QString &text) override
Finds completions to the given text.
virtual void setMode(Mode mode)
Changes the completion mode: exe or file completion.
bool endsWith(QChar c, Qt::CaseSensitivity cs) const const
qsizetype indexOf(QChar ch, qsizetype from, Qt::CaseSensitivity cs) const const
QString & insert(qsizetype position, QChar ch)
bool isNull() const const
QString left(qsizetype n) const const
qsizetype length() const const
QString mid(qsizetype position, qsizetype n) const const
QString & prepend(QChar ch)
QString & replace(QChar before, QChar after, Qt::CaseSensitivity cs)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 18 2024 12:16:28 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.