KIO
kshellcompletion.cpp
Go to the documentation of this file.
00001 /* This file is part of the KDE libraries 00002 Copyright (C) 2000 David Smith <dsmith@algonet.se> 00003 00004 This library is free software; you can redistribute it and/or 00005 modify it under the terms of the GNU Library General Public 00006 License as published by the Free Software Foundation; either 00007 version 2 of the License, or (at your option) any later version. 00008 00009 This library is distributed in the hope that it will be useful, 00010 but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00012 Library General Public License for more details. 00013 00014 You should have received a copy of the GNU Library General Public License 00015 along with this library; see the file COPYING.LIB. If not, write to 00016 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00017 Boston, MA 02110-1301, USA. 00018 */ 00019 00020 #include "kshellcompletion.h" 00021 00022 #include <stdlib.h> 00023 #include <kdebug.h> 00024 #include <QtCore/QCharRef> 00025 #include <QtCore/QMutableStringListIterator> 00026 #include <QtCore/QRegExp> 00027 #include <kcompletion.h> 00028 00029 class KShellCompletionPrivate 00030 { 00031 public: 00032 KShellCompletionPrivate() 00033 : m_word_break_char(' ') 00034 , m_quote_char1( '\"' ) 00035 , m_quote_char2( '\'' ) 00036 , m_escape_char( '\\' ) 00037 { 00038 } 00039 00040 void splitText(const QString &text, QString &text_start, QString &text_compl) const; 00041 bool quoteText(QString *text, bool force, bool skip_last) const; 00042 QString unquote(const QString &text) const; 00043 00044 QString m_text_start; // part of the text that was not completed 00045 QString m_text_compl; // part of the text that was completed (unchanged) 00046 00047 QChar m_word_break_char; 00048 QChar m_quote_char1; 00049 QChar m_quote_char2; 00050 QChar m_escape_char; 00051 }; 00052 00053 KShellCompletion::KShellCompletion() 00054 : KUrlCompletion(), 00055 d( new KShellCompletionPrivate ) 00056 { 00057 } 00058 00059 KShellCompletion::~KShellCompletion() 00060 { 00061 delete d; 00062 } 00063 00064 00065 /* 00066 * makeCompletion() 00067 * 00068 * Entry point for file name completion 00069 */ 00070 QString KShellCompletion::makeCompletion(const QString &text) 00071 { 00072 // Split text at the last unquoted space 00073 // 00074 d->splitText(text, d->m_text_start, d->m_text_compl); 00075 00076 // Remove quotes from the text to be completed 00077 // 00078 QString tmp = d->unquote(d->m_text_compl); 00079 d->m_text_compl = tmp; 00080 00081 // Do exe-completion if there was no unquoted space 00082 // 00083 bool is_exe_completion = true; 00084 00085 for ( int i = 0; i < d->m_text_start.length(); i++ ) { 00086 if ( d->m_text_start[i] != d->m_word_break_char ) { 00087 is_exe_completion = false; 00088 break; 00089 } 00090 } 00091 00092 Mode mode = (is_exe_completion ? ExeCompletion : FileCompletion ); 00093 00094 setMode(mode); 00095 00096 // Make completion on the last part of text 00097 // 00098 return KUrlCompletion::makeCompletion( d->m_text_compl ); 00099 } 00100 00101 /* 00102 * postProcessMatch, postProcessMatches 00103 * 00104 * Called by KCompletion before emitting match() and matches() 00105 * 00106 * Add add the part of the text that was not completed 00107 * Add quotes when needed 00108 */ 00109 void KShellCompletion::postProcessMatch( QString *match ) const 00110 { 00111 KUrlCompletion::postProcessMatch( match ); 00112 00113 if ( match->isNull() ) 00114 return; 00115 00116 if ( match->endsWith( QLatin1Char( '/' ) ) ) 00117 d->quoteText( match, false, true ); // don't quote the trailing '/' 00118 else 00119 d->quoteText( match, false, false ); // quote the whole text 00120 00121 match->prepend( d->m_text_start ); 00122 } 00123 00124 void KShellCompletion::postProcessMatches( QStringList *matches ) const 00125 { 00126 KUrlCompletion::postProcessMatches( matches ); 00127 00128 for ( QStringList::Iterator it = matches->begin(); 00129 it != matches->end(); ++it ) 00130 { 00131 if ( !(*it).isNull() ) { 00132 if ( (*it).endsWith( QLatin1Char( '/' ) ) ) 00133 d->quoteText( &(*it), false, true ); // don't quote trailing '/' 00134 else 00135 d->quoteText( &(*it), false, false ); // quote the whole text 00136 00137 (*it).prepend( d->m_text_start ); 00138 } 00139 } 00140 } 00141 00142 void KShellCompletion::postProcessMatches( KCompletionMatches *matches ) const 00143 { 00144 KUrlCompletion::postProcessMatches( matches ); 00145 00146 for ( KCompletionMatches::Iterator it = matches->begin(); 00147 it != matches->end(); ++it ) 00148 { 00149 if ( !(*it).value().isNull() ) { 00150 if ( (*it).value().endsWith( QLatin1Char( '/' ) ) ) 00151 d->quoteText( &(*it).value(), false, true ); // don't quote trailing '/' 00152 else 00153 d->quoteText( &(*it).value(), false, false ); // quote the whole text 00154 00155 (*it).value().prepend( d->m_text_start ); 00156 } 00157 } 00158 } 00159 00160 /* 00161 * splitText 00162 * 00163 * Split text at the last unquoted space 00164 * 00165 * text_start = [out] text at the left, including the space 00166 * text_compl = [out] text at the right 00167 */ 00168 void KShellCompletionPrivate::splitText(const QString &text, QString &text_start, 00169 QString &text_compl) const 00170 { 00171 bool in_quote = false; 00172 bool escaped = false; 00173 QChar p_last_quote_char; 00174 int last_unquoted_space = -1; 00175 int end_space_len = 0; 00176 00177 for (int pos = 0; pos < text.length(); pos++) { 00178 00179 end_space_len = 0; 00180 00181 if ( escaped ) { 00182 escaped = false; 00183 } 00184 else if ( in_quote && text[pos] == p_last_quote_char ) { 00185 in_quote = false; 00186 } 00187 else if ( !in_quote && text[pos] == m_quote_char1 ) { 00188 p_last_quote_char = m_quote_char1; 00189 in_quote = true; 00190 } 00191 else if ( !in_quote && text[pos] == m_quote_char2 ) { 00192 p_last_quote_char = m_quote_char2; 00193 in_quote = true; 00194 } 00195 else if ( text[pos] == m_escape_char ) { 00196 escaped = true; 00197 } 00198 else if ( !in_quote && text[pos] == m_word_break_char ) { 00199 00200 end_space_len = 1; 00201 00202 while ( pos+1 < text.length() && text[pos+1] == m_word_break_char ) { 00203 end_space_len++; 00204 pos++; 00205 } 00206 00207 if ( pos+1 == text.length() ) 00208 break; 00209 00210 last_unquoted_space = pos; 00211 } 00212 } 00213 00214 text_start = text.left( last_unquoted_space + 1 ); 00215 00216 // the last part without trailing blanks 00217 text_compl = text.mid( last_unquoted_space + 1 ); 00218 } 00219 00220 /* 00221 * quoteText() 00222 * 00223 * Add quotations to 'text' if needed or if 'force' = true 00224 * Returns true if quotes were added 00225 * 00226 * skip_last => ignore the last charachter (we add a space or '/' to all filenames) 00227 */ 00228 bool KShellCompletionPrivate::quoteText(QString *text, bool force, bool skip_last) const 00229 { 00230 int pos = 0; 00231 00232 if ( !force ) { 00233 pos = text->indexOf( m_word_break_char ); 00234 if ( skip_last && (pos == (int)(text->length())-1) ) pos = -1; 00235 } 00236 00237 if ( !force && pos == -1 ) { 00238 pos = text->indexOf( m_quote_char1 ); 00239 if ( skip_last && (pos == (int)(text->length())-1) ) pos = -1; 00240 } 00241 00242 if ( !force && pos == -1 ) { 00243 pos = text->indexOf( m_quote_char2 ); 00244 if ( skip_last && (pos == (int)(text->length())-1) ) pos = -1; 00245 } 00246 00247 if ( !force && pos == -1 ) { 00248 pos = text->indexOf( m_escape_char ); 00249 if ( skip_last && (pos == (int)(text->length())-1) ) pos = -1; 00250 } 00251 00252 if ( force || (pos >= 0) ) { 00253 00254 // Escape \ in the string 00255 text->replace( m_escape_char, 00256 QString( m_escape_char ) + m_escape_char ); 00257 00258 // Escape " in the string 00259 text->replace( m_quote_char1, 00260 QString( m_escape_char ) + m_quote_char1 ); 00261 00262 // " at the beginning 00263 text->insert( 0, m_quote_char1 ); 00264 00265 // " at the end 00266 if ( skip_last ) 00267 text->insert( text->length()-1, m_quote_char1 ); 00268 else 00269 text->insert( text->length(), m_quote_char1 ); 00270 00271 return true; 00272 } 00273 00274 return false; 00275 } 00276 00277 /* 00278 * unquote 00279 * 00280 * Remove quotes and return the result in a new string 00281 * 00282 */ 00283 QString KShellCompletionPrivate::unquote(const QString &text) const 00284 { 00285 bool in_quote = false; 00286 bool escaped = false; 00287 QChar p_last_quote_char; 00288 QString result; 00289 00290 for (int pos = 0; pos < text.length(); pos++) { 00291 00292 if ( escaped ) { 00293 escaped = false; 00294 result.insert( result.length(), text[pos] ); 00295 } 00296 else if ( in_quote && text[pos] == p_last_quote_char ) { 00297 in_quote = false; 00298 } 00299 else if ( !in_quote && text[pos] == m_quote_char1 ) { 00300 p_last_quote_char = m_quote_char1; 00301 in_quote = true; 00302 } 00303 else if ( !in_quote && text[pos] == m_quote_char2 ) { 00304 p_last_quote_char = m_quote_char2; 00305 in_quote = true; 00306 } 00307 else if ( text[pos] == m_escape_char ) { 00308 escaped = true; 00309 result.insert( result.length(), text[pos] ); 00310 } 00311 else { 00312 result.insert( result.length(), text[pos] ); 00313 } 00314 00315 } 00316 00317 return result; 00318 } 00319 00320 #include "kshellcompletion.moc" 00321
KDE 4.6 API Reference