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