1 /**
2 * \file readlinecompleter.cpp
3 * Abstract base class for readline completer.
4 *
5 * \b Project: Kid3
6 * \author Urs Fleisch
7 * \date 22 Sep 2013
8 *
9 * Copyright (C) 2013-2018 Urs Fleisch
10 *
11 * This file is part of Kid3.
12 *
13 * Kid3 is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or
16 * (at your option) any later version.
17 *
18 * Kid3 is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program. If not, see <http://www.gnu.org/licenses/>.
25 */
26
27 #include "readlinecompleter.h"
28
29 #include <cstdio>
30 #include <readline/readline.h>
31 #include <cstdlib>
32
33 /** Installed readline completer. */
34 ReadlineCompleter* ReadlineCompleter::s_completer = nullptr;
35
36 /**
37 * Destructor.
38 */
~ReadlineCompleter()39 ReadlineCompleter::~ReadlineCompleter()
40 {
41 }
42
43 /**
44 * Install this completer to be used with readline.
45 */
install()46 void ReadlineCompleter::install()
47 {
48 s_completer = this;
49 ::rl_attempted_completion_function = completion;
50 #if RL_READLINE_VERSION > 0x0402
51 ::rl_completer_quote_characters = "\"";
52 ::rl_filename_quote_characters = " '\"\\\t";
53 #else
54 ::rl_completer_quote_characters = const_cast<char*>("\"");
55 #endif
56 }
57
58 /**
59 * Readline completion function.
60 * @param text contents to complete
61 * @param start start index in rl_line_buffer of word to complete
62 * @param end end index in rl_line_buffer of word to complete
63 * @return array of matches or 0 if there aren't any.
64 */
completion(const char * text,int start,int end)65 char** ReadlineCompleter::completion(const char* text, int start, int end)
66 {
67 Q_UNUSED(end)
68 char** matches = nullptr;
69
70 if (start == 0) {
71 matches = ::rl_completion_matches(text, commandGenerator);
72 } else if (s_completer) {
73 if (s_completer->updateParameterList(::rl_line_buffer)) {
74 matches = ::rl_completion_matches(text, parameterGenerator);
75 if (!matches) {
76 ::rl_attempted_completion_over = 1;
77 }
78 } else {
79 #if RL_READLINE_VERSION > 0x0402
80 ::rl_filename_quoting_desired = 1;
81 #endif
82 }
83 }
84
85 return matches;
86 }
87
88 /**
89 * Readline command generator.
90 * @param text partial word to be completed
91 * @param state 0 for first call, >0 for subsequent calls
92 * @return next completion string, allocated with malloc(), 0 if there are
93 * no more possibilities left.
94 */
commandGenerator(const char * text,int state)95 char* ReadlineCompleter::commandGenerator(const char* text, int state)
96 {
97 if (s_completer) {
98 return completionGenerator(s_completer->getCommandList(), text, state);
99 }
100 return nullptr;
101 }
102
103 /**
104 * Readline parameter generator.
105 * @param text partial word to be completed
106 * @param state 0 for first call, >0 for subsequent calls
107 * @return next completion string, allocated with malloc(), 0 if there are
108 * no more possibilities left.
109 */
parameterGenerator(const char * text,int state)110 char* ReadlineCompleter::parameterGenerator(const char* text, int state)
111 {
112 if (s_completer) {
113 return completionGenerator(s_completer->getParameterList(), text, state);
114 }
115 return nullptr;
116 }
117
118 /**
119 * Readline completion generator.
120 * @param completions list of completions
121 * @param text partial word to be completed
122 * @param state 0 for first call, >0 for subsequent calls
123 * @return next completion string, allocated with malloc(), 0 if there are
124 * no more possibilities left.
125 */
completionGenerator(const QList<QByteArray> & completions,const char * text,int state)126 char* ReadlineCompleter::completionGenerator(
127 const QList<QByteArray>& completions, const char* text, int state)
128 {
129 static int listIndex, textLen;
130 if (state == 0) {
131 listIndex = 0;
132 textLen = qstrlen(text);
133 }
134
135 while (listIndex < completions.size()) {
136 const QByteArray& name = completions.at(listIndex++);
137 if (name.left(textLen) == text) {
138 auto r = reinterpret_cast<char*>(::malloc(name.length() + 1));
139 qstrcpy(r, name.constData());
140 return r;
141 }
142 }
143
144 return nullptr;
145 }
146