File indexing completed on 2024-05-19 04:55:50

0001 /**
0002  * \file readlinecompleter.cpp
0003  * Abstract base class for readline completer.
0004  *
0005  * \b Project: Kid3
0006  * \author Urs Fleisch
0007  * \date 22 Sep 2013
0008  *
0009  * Copyright (C) 2013-2024  Urs Fleisch
0010  *
0011  * This file is part of Kid3.
0012  *
0013  * Kid3 is free software; you can redistribute it and/or modify
0014  * it under the terms of the GNU General Public License as published by
0015  * the Free Software Foundation; either version 2 of the License, or
0016  * (at your option) any later version.
0017  *
0018  * Kid3 is distributed in the hope that it will be useful,
0019  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0020  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0021  * GNU General Public License for more details.
0022  *
0023  * You should have received a copy of the GNU General Public License
0024  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
0025  */
0026 
0027 #include "readlinecompleter.h"
0028 
0029 #include <cstdio>
0030 #include <readline/readline.h>
0031 #include <cstdlib>
0032 
0033 /** Installed readline completer. */
0034 ReadlineCompleter* ReadlineCompleter::s_completer = nullptr;
0035 
0036 /**
0037  * Destructor.
0038  */
0039 ReadlineCompleter::~ReadlineCompleter()
0040 {
0041 }
0042 
0043 /**
0044  * Install this completer to be used with readline.
0045  */
0046 void ReadlineCompleter::install()
0047 {
0048   s_completer = this;
0049   ::rl_attempted_completion_function = completion;
0050 #if RL_READLINE_VERSION > 0x0402
0051   ::rl_completer_quote_characters = "\"";
0052   ::rl_filename_quote_characters = " '\"\\\t";
0053 #else
0054   ::rl_completer_quote_characters = const_cast<char*>("\"");
0055 #endif
0056 }
0057 
0058 /**
0059  * Readline completion function.
0060  * @param text contents to complete
0061  * @param start start index in rl_line_buffer of word to complete
0062  * @param end end index in rl_line_buffer of word to complete
0063  * @return array of matches or 0 if there aren't any.
0064  */
0065 char** ReadlineCompleter::completion(const char* text, int start, int end)
0066 {
0067   Q_UNUSED(end)
0068   char** matches = nullptr;
0069 
0070   if (start == 0) {
0071     matches = ::rl_completion_matches(text, commandGenerator);
0072   } else if (s_completer) {
0073     if (s_completer->updateParameterList(::rl_line_buffer)) {
0074       matches = ::rl_completion_matches(text, parameterGenerator);
0075       if (!matches) {
0076         ::rl_attempted_completion_over = 1;
0077       }
0078     } else {
0079 #if RL_READLINE_VERSION > 0x0402
0080       ::rl_filename_quoting_desired = 1;
0081 #endif
0082     }
0083   }
0084 
0085   return matches;
0086 }
0087 
0088 /**
0089  * Readline command generator.
0090  * @param text partial word to be completed
0091  * @param state 0 for first call, >0 for subsequent calls
0092  * @return next completion string, allocated with malloc(), 0 if there are
0093  * no more possibilities left.
0094  */
0095 char* ReadlineCompleter::commandGenerator(const char* text, int state)
0096 {
0097   if (s_completer) {
0098     return completionGenerator(s_completer->getCommandList(), text, state);
0099   }
0100   return nullptr;
0101 }
0102 
0103 /**
0104  * Readline parameter generator.
0105  * @param text partial word to be completed
0106  * @param state 0 for first call, >0 for subsequent calls
0107  * @return next completion string, allocated with malloc(), 0 if there are
0108  * no more possibilities left.
0109  */
0110 char* ReadlineCompleter::parameterGenerator(const char* text, int state)
0111 {
0112   if (s_completer) {
0113     return completionGenerator(s_completer->getParameterList(), text, state);
0114   }
0115   return nullptr;
0116 }
0117 
0118 /**
0119  * Readline completion generator.
0120  * @param completions list of completions
0121  * @param text partial word to be completed
0122  * @param state 0 for first call, >0 for subsequent calls
0123  * @return next completion string, allocated with malloc(), 0 if there are
0124  * no more possibilities left.
0125  */
0126 char* ReadlineCompleter::completionGenerator(
0127     const QList<QByteArray>& completions, const char* text, int state)
0128 {
0129   static int listIndex, textLen;
0130   if (state == 0) {
0131     listIndex = 0;
0132     textLen = qstrlen(text);
0133   }
0134 
0135   while (listIndex < completions.size()) {
0136     if (const QByteArray& name = completions.at(listIndex++);
0137         name.left(textLen) == text) {
0138       auto r = static_cast<char*>(::malloc(name.length() + 1));
0139       qstrcpy(r, name.constData());
0140       return r;
0141     }
0142   }
0143 
0144   return nullptr;
0145 }