mirror of
https://github.com/Mercury-Language/mercury.git
synced 2026-04-15 01:13:30 +00:00
The Windows _snprintf family of functions do not guarantee null
termination when the output is truncated so cannot be used as direct
replacements for the snprintf functions. Also, the _snprintf functions
have different return values from the C99 snprintf functions when output
is truncated (like some older snprintf implementations).
Furthermore, on Windows snprintf/vsnprintf may be synonyms for
_snprintf/_vsnprintf so cannot be relied upon to terminate their outputs
either, even if the functions exist.
runtime/mercury_string.c:
runtime/mercury_string.h:
Define MR_snprintf and MR_vsnprintf as macro synonyms for
snprintf/vsnprintf ONLY if _snprintf/_vsnprintf do not exist.
Otherwise, implement MR_snprintf and MR_vsnprintf functions
that behave like the C99 functions, in terms of _vsnprintf.
Require that either snprintf/vsnprintf or _snprintf/_vsnprintf
are available. This should be true on all systems still in use.
runtime/mercury_debug.c:
runtime/mercury_ml_expand_body.h:
runtime/mercury_runtime_util.c:
runtime/mercury_stack_layout.c:
runtime/mercury_stack_trace.c:
runtime/mercury_stacks.c:
runtime/mercury_tabling.c:
runtime/mercury_threadscope.c:
runtime/mercury_trace_base.c:
runtime/mercury_wrapper.c:
trace/mercury_trace_completion.c:
trace/mercury_trace_internal.c:
trace/mercury_trace_spy.c:
trace/mercury_trace_vars.c:
bytecode/mb_disasm.c:
Use MR_snprintf instead of snprintf/_snprintf
and MR_vsnprintf instead of vsnprintf/_vsnprintf.
Drop code paths using sprintf as a fallback.
733 lines
24 KiB
C
733 lines
24 KiB
C
// vim: ts=4 sw=4 expandtab ft=c
|
|
|
|
// Copyright (C) 2002, 2005-2006 The University of Melbourne.
|
|
// Copyright (C) 2014-2016, 2018 The Mercury team.
|
|
// This file is distributed under the terms specified in COPYING.LIB.
|
|
|
|
// mercury_trace_completion.c
|
|
//
|
|
// Main author: stayl
|
|
//
|
|
// Command line completion for mdb.
|
|
|
|
#include "mercury_memory.h"
|
|
#include "mercury_std.h"
|
|
#include "mercury_string.h"
|
|
#include "mercury_array_macros.h"
|
|
#include "mercury_trace_completion.h"
|
|
#include "mercury_trace_internal.h"
|
|
#include "mercury_trace_alias.h"
|
|
#include "mercury_trace_tables.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#if defined(MR_USE_READLINE) || defined(MR_USE_EDITLINE)
|
|
#if defined(MR_HAVE_READLINE_READLINE_H)
|
|
#include <readline/readline.h>
|
|
#elif defined(MR_HAVE_EDITLINE_READLINE_H)
|
|
#include <editline/readline.h>
|
|
#else
|
|
extern char *rl_line_buffer;
|
|
extern int rl_point;
|
|
extern char *rl_filename_completion_function(const char *word, int state);
|
|
#endif
|
|
#endif
|
|
|
|
static char *MR_prepend_string(char *completion,
|
|
MR_CompleterData *data);
|
|
|
|
static char *MR_trace_completer_list_next(const char *word,
|
|
size_t word_len, MR_CompleterList **list);
|
|
static void MR_trace_free_completer_list(
|
|
MR_CompleterList *completer_list);
|
|
|
|
static char *MR_trace_sorted_array_completer_next(
|
|
const char *word, size_t word_length,
|
|
MR_CompleterData *data);
|
|
|
|
static MR_CompleterList *MR_trace_string_array_completer(
|
|
const char *const *strings);
|
|
static char *MR_trace_string_array_completer_next(
|
|
const char *word, size_t word_len,
|
|
MR_CompleterData *data);
|
|
|
|
static int MR_compare_source_file_lines(
|
|
const void *ptr1, const void *ptr2);
|
|
static void MR_insert_module_into_source_file_line_table(
|
|
const MR_ModuleLayout *module);
|
|
|
|
static char *MR_trace_filename_completer_next(const char *word,
|
|
size_t word_len, MR_CompleterData *);
|
|
|
|
static char *MR_trace_filter_completer_next(const char *word,
|
|
size_t word_len, MR_CompleterData *);
|
|
static void MR_trace_free_filter_completer_data(
|
|
MR_CompleterData data);
|
|
|
|
static char *MR_trace_map_completer_next(const char *word,
|
|
size_t word_len, MR_CompleterData *);
|
|
static void MR_trace_free_map_completer_data(
|
|
MR_CompleterData data);
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
// Called by Readline when the user requests completion.
|
|
// Examine Readline's input buffer to work out which completers
|
|
// should be used, then apply them.
|
|
// Readline passes zero for `state' on the first call, and non-zero
|
|
// on subsequent calls.
|
|
|
|
char *
|
|
MR_trace_line_completer(const char *passed_word, int state)
|
|
{
|
|
#if !defined(MR_USE_READLINE) && !defined(MR_USE_EDITLINE)
|
|
return NULL;
|
|
#else
|
|
static MR_CompleterList *completer_list;
|
|
static char *word;
|
|
static size_t word_len;
|
|
char *completion;
|
|
|
|
// If `state' is 0, this is the first call for this `word',
|
|
// so set up the list of completers.
|
|
|
|
if (state == 0) {
|
|
char *line;
|
|
char *command_end;
|
|
char *command_start;
|
|
char *insertion_point;
|
|
char *semicolon;
|
|
|
|
MR_trace_free_completer_list(completer_list);
|
|
completer_list = NULL;
|
|
if (word != NULL) {
|
|
MR_free(word);
|
|
}
|
|
|
|
line = rl_line_buffer;
|
|
insertion_point = rl_line_buffer + rl_point;
|
|
|
|
// There may be multiple commands in the line.
|
|
// Skip to the one we are trying to complete.
|
|
|
|
semicolon = strchr(line, ';');
|
|
while (semicolon != NULL && semicolon < insertion_point) {
|
|
line = semicolon + 1;
|
|
semicolon = strchr(line, ';');
|
|
}
|
|
|
|
// Skip space or a number at the beginning of the command.
|
|
while (line < insertion_point &&
|
|
(MR_isspace(*line) || MR_isdigit(*line)))
|
|
{
|
|
line++;
|
|
}
|
|
|
|
// Find the end of the command.
|
|
command_start = line;
|
|
command_end = line;
|
|
while (command_end < insertion_point && !MR_isspace(*command_end)) {
|
|
command_end++;
|
|
}
|
|
|
|
if (command_end == insertion_point) {
|
|
// We are completing the command itself.
|
|
int num_digits;
|
|
char *digits;
|
|
MR_CompleterList *command_completer;
|
|
MR_CompleterList *alias_completer;
|
|
|
|
// Strip off any number preceding the command
|
|
// (it will need to be added back later).
|
|
|
|
num_digits = 0;
|
|
while (MR_isdigit(passed_word[num_digits])) {
|
|
num_digits++;
|
|
}
|
|
word = MR_copy_string(passed_word + num_digits);
|
|
word_len = strlen(word);
|
|
|
|
// Set up completers for commands and aliases.
|
|
command_completer = MR_trace_command_completer(word, word_len);
|
|
alias_completer = MR_trace_alias_completer(word, word_len);
|
|
|
|
completer_list = command_completer;
|
|
completer_list->MR_completer_list_next = alias_completer;
|
|
|
|
// Add back the preceding number to the completions.
|
|
if (num_digits != 0) {
|
|
digits = MR_malloc(num_digits + 1);
|
|
strncpy(digits, passed_word, num_digits);
|
|
digits[num_digits] = '\0';
|
|
completer_list = MR_trace_map_completer(MR_prepend_string,
|
|
digits, MR_free_func, completer_list);
|
|
}
|
|
} else {
|
|
// We are completing an argument of the command.
|
|
#define MR_MAX_COMMAND_NAME_LEN 256
|
|
char command[MR_MAX_COMMAND_NAME_LEN];
|
|
char *expanded_command;
|
|
int command_len;
|
|
char **words;
|
|
int word_count;
|
|
MR_MakeCompleter command_completer;
|
|
const char *const *command_fixed_args;
|
|
MR_CompleterList *arg_completer;
|
|
|
|
command_len = command_end - command_start;
|
|
if (command_len >= MR_MAX_COMMAND_NAME_LEN) {
|
|
// The string is too long to be a command.
|
|
return NULL;
|
|
} else {
|
|
strncpy(command, command_start, command_len);
|
|
command[command_len] = '\0';
|
|
}
|
|
|
|
// Expand aliases.
|
|
if (MR_trace_lookup_alias(command, &words, &word_count)) {
|
|
if (word_count == 0) {
|
|
return NULL;
|
|
} else {
|
|
expanded_command = words[0];
|
|
}
|
|
} else {
|
|
expanded_command = command;
|
|
}
|
|
|
|
if (! MR_trace_command_completion_info(expanded_command,
|
|
&command_completer, &command_fixed_args))
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
// Set up a completer for the fixed argument strings.
|
|
completer_list = NULL;
|
|
if (command_fixed_args != NULL) {
|
|
completer_list = MR_trace_string_array_completer(
|
|
command_fixed_args);
|
|
}
|
|
|
|
word = MR_copy_string(passed_word);
|
|
word_len = strlen(word);
|
|
|
|
// Set up a completer for the non-fixed argument strings.
|
|
arg_completer = (*command_completer)(word, word_len);
|
|
if (completer_list == NULL) {
|
|
completer_list = arg_completer;
|
|
} else {
|
|
completer_list->MR_completer_list_next = arg_completer;
|
|
}
|
|
}
|
|
}
|
|
|
|
completion = MR_trace_completer_list_next(word, word_len, &completer_list);
|
|
if (completion == NULL) {
|
|
MR_trace_free_completer_list(completer_list);
|
|
MR_free(word);
|
|
word = NULL;
|
|
}
|
|
|
|
return completion;
|
|
#endif // defined(MR_USE_READLINE) || defined(MR_USE_EDITLINE)
|
|
}
|
|
|
|
static char *
|
|
MR_prepend_string(char *string, MR_CompleterData *data)
|
|
{
|
|
char *string_to_prepend;
|
|
size_t string_to_prepend_len;
|
|
char *result;
|
|
|
|
string_to_prepend = (char *) *data;
|
|
string_to_prepend_len = strlen(string_to_prepend);
|
|
result = MR_malloc(string_to_prepend_len + strlen(string) + 1);
|
|
strcpy(result, string_to_prepend);
|
|
strcpy(result + string_to_prepend_len, string);
|
|
MR_free(string);
|
|
return result;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
static char *
|
|
MR_trace_completer_list_next(const char *word, size_t word_len,
|
|
MR_CompleterList **list)
|
|
{
|
|
MR_CompleterList *current_completer;
|
|
char *result;
|
|
|
|
if (list == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
while (1) {
|
|
current_completer = *list;
|
|
if (current_completer == NULL) {
|
|
return NULL;
|
|
}
|
|
result = (current_completer->MR_completer_func)(word, word_len,
|
|
&(current_completer->MR_completer_func_data));
|
|
if (result != NULL) {
|
|
return result;
|
|
} else {
|
|
*list = current_completer->MR_completer_list_next;
|
|
(current_completer->MR_free_completer_func_data)(
|
|
current_completer->MR_completer_func_data);
|
|
MR_free(current_completer);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
MR_trace_free_completer_list(MR_CompleterList *completer_list)
|
|
{
|
|
MR_CompleterList *tmp_list;
|
|
|
|
while (completer_list != NULL) {
|
|
tmp_list = completer_list;
|
|
completer_list = completer_list->MR_completer_list_next;
|
|
(tmp_list->MR_free_completer_func_data)(
|
|
tmp_list->MR_completer_func_data);
|
|
MR_free(tmp_list);
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
// No completions.
|
|
|
|
MR_CompleterList *
|
|
MR_trace_null_completer(const char *word, size_t word_len)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
// Complete on the labels of a sorted array.
|
|
|
|
typedef struct {
|
|
MR_GetSlotName MR_sorted_array_get_slot_name;
|
|
int MR_sorted_array_current_offset;
|
|
int MR_sorted_array_size;
|
|
} MR_SortedArrayCompleterData;
|
|
|
|
MR_CompleterList *
|
|
MR_trace_sorted_array_completer(const char *word, size_t word_length,
|
|
int array_size, MR_GetSlotName get_slot_name)
|
|
{
|
|
MR_CompleterList *completer;
|
|
MR_bool found;
|
|
int slot;
|
|
MR_SortedArrayCompleterData *data;
|
|
|
|
// Find the slot containing the first possible match, optimizing for the
|
|
// common case where we are finding all elements in the array.
|
|
|
|
if (word_length == 0) {
|
|
found = (array_size != 0);
|
|
slot = 0;
|
|
} else {
|
|
MR_find_first_match(array_size, slot, found,
|
|
strncmp(get_slot_name(slot), word, word_length));
|
|
}
|
|
|
|
if (found) {
|
|
data = MR_NEW(MR_SortedArrayCompleterData);
|
|
data->MR_sorted_array_get_slot_name = get_slot_name;
|
|
data->MR_sorted_array_current_offset = slot;
|
|
data->MR_sorted_array_size = array_size;
|
|
completer = MR_new_completer_elem(MR_trace_sorted_array_completer_next,
|
|
(MR_CompleterData) data, MR_free_func);
|
|
} else {
|
|
completer = NULL;
|
|
}
|
|
return completer;
|
|
}
|
|
|
|
static char *
|
|
MR_trace_sorted_array_completer_next(const char *word,
|
|
size_t word_length, MR_CompleterData *completer_data)
|
|
{
|
|
MR_SortedArrayCompleterData *data;
|
|
char *completion;
|
|
|
|
data = (MR_SortedArrayCompleterData *) *completer_data;
|
|
|
|
if (data->MR_sorted_array_current_offset < data->MR_sorted_array_size) {
|
|
completion = data->MR_sorted_array_get_slot_name(
|
|
data->MR_sorted_array_current_offset);
|
|
if (MR_strneq(completion, word, word_length)) {
|
|
data->MR_sorted_array_current_offset++;
|
|
return MR_copy_string(completion);
|
|
} else {
|
|
return NULL;
|
|
}
|
|
} else {
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
// Complete on the elements of an unsorted array of strings.
|
|
|
|
typedef struct MR_StringArrayCompleterData_struct {
|
|
char **MR_string_array;
|
|
int MR_string_array_current_offset;
|
|
} MR_StringArrayCompleterData;
|
|
|
|
// Complete on a NULL terminated array of strings.
|
|
// The strings will not be `free'd.
|
|
|
|
static MR_CompleterList *
|
|
MR_trace_string_array_completer(const char *const *strings)
|
|
{
|
|
MR_StringArrayCompleterData *data;
|
|
|
|
data = MR_NEW(MR_StringArrayCompleterData);
|
|
data->MR_string_array = (char **) strings;
|
|
data->MR_string_array_current_offset = 0;
|
|
return MR_new_completer_elem(&MR_trace_string_array_completer_next,
|
|
(MR_CompleterData) data, MR_free_func);
|
|
}
|
|
|
|
static char *
|
|
MR_trace_string_array_completer_next(const char *word, size_t word_len,
|
|
MR_CompleterData *data)
|
|
{
|
|
MR_StringArrayCompleterData *completer_data;
|
|
char *result;
|
|
|
|
completer_data = (MR_StringArrayCompleterData *) *data;
|
|
|
|
while (1) {
|
|
result = completer_data->MR_string_array[
|
|
completer_data->MR_string_array_current_offset];
|
|
completer_data->MR_string_array_current_offset++;
|
|
if (result == NULL) {
|
|
return NULL;
|
|
} else {
|
|
if (strncmp(result, word, word_len) == 0) {
|
|
return MR_copy_string(result);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
// Complete on either a procedure specification, or a filename:linenumber pair.
|
|
//
|
|
// We construct the sorted array of filename:linenumber pairs by first
|
|
// adding all such pairs implied by the module layout structures of the Mercury
|
|
// modules that have debugging information to MR_source_file_lines,
|
|
// and then sorting that array. We cannot use the macros defined in
|
|
// mercury_array_util.h to keep the array sorted *during* the insertion
|
|
// process, because the array can gen very big (more than 150,000 entries
|
|
// for the Mercury compiler itself), and performing O(n) calls to
|
|
// MR_prepare_insert_into_sorted, whose complexity is itself O(n),
|
|
// would yield a quadratic algorithm.' We do the sorting using qsort,
|
|
// whose expected complexity is O(n log n). (I don't expect its worst case
|
|
// O(n^2) performance to ever occur in practice.)
|
|
//
|
|
// Since the MR_source_file_lines array is not sorted while we insert into it,
|
|
// we have no cheap way to search the entirety of the so-far filled in parts
|
|
// of the array to see whether the string we are about to add to it already
|
|
// occurs in it. However, we can stop most duplicates from ever getting into
|
|
// the array by simply checking if the string we are about to add is for
|
|
// the same line number in the same file as the previous string, and we do
|
|
// a final pass over the array after it is sorted, squeezing out the remaining
|
|
// duplicates, which are now guaranteed to be adjacent to each other.
|
|
|
|
#define INIT_SOURCE_FILE_LINE_TABLE_SIZE 10
|
|
|
|
static const char **MR_source_file_lines;
|
|
static unsigned MR_source_file_line_next = 0;
|
|
static unsigned MR_source_file_line_max = 0;
|
|
static MR_bool MR_source_file_lines_initialized = MR_FALSE;
|
|
|
|
#define INIT_SOURCE_FILE_LINE_CHARS_SIZE 100
|
|
#define LINE_NUM_MAX_CHARS 20
|
|
|
|
static char *MR_source_file_line_chars;
|
|
static unsigned MR_source_file_line_char_next = 0;
|
|
static unsigned MR_source_file_line_char_max = 0;
|
|
|
|
static void
|
|
MR_insert_module_into_source_file_line_table(const MR_ModuleLayout *module)
|
|
{
|
|
int num_files;
|
|
int cur_file;
|
|
|
|
num_files = module->MR_ml_filename_count;
|
|
for (cur_file = 0; cur_file < num_files; cur_file++) {
|
|
const MR_ModuleFileLayout *file;
|
|
MR_ConstString file_name;
|
|
int file_name_len;
|
|
int num_lines;
|
|
int cur_line;
|
|
|
|
file = module->MR_ml_module_file_layout[cur_file];
|
|
file_name = file->MR_mfl_filename;
|
|
file_name_len = strlen(file_name);
|
|
|
|
MR_ensure_big_enough(file_name_len + LINE_NUM_MAX_CHARS + 2,
|
|
MR_source_file_line_char, char,
|
|
INIT_SOURCE_FILE_LINE_CHARS_SIZE);
|
|
strcpy(MR_source_file_line_chars, file_name);
|
|
MR_source_file_line_chars[file_name_len] = ':';
|
|
|
|
num_lines = file->MR_mfl_label_count;
|
|
// The +1 is for the sentinel.
|
|
MR_ensure_big_enough(MR_source_file_line_next + num_lines + 1,
|
|
MR_source_file_line, const char *,
|
|
INIT_SOURCE_FILE_LINE_TABLE_SIZE);
|
|
for (cur_line = 0; cur_line < num_lines; cur_line++) {
|
|
int line_num = file->MR_mfl_label_lineno[cur_line];
|
|
|
|
// Almost all duplicates that end up in MR_source_file_lines
|
|
// get there from consecutive entries for the same line number
|
|
// in the same module file layout structure. Look for this
|
|
// relatively common case, and avoid adding the redundant entry
|
|
// to the array if we find it.
|
|
|
|
if (cur_line > 0 &&
|
|
line_num == file->MR_mfl_label_lineno[cur_line - 1])
|
|
{
|
|
// The string we would add would be the same as the string
|
|
// for the previous line number.
|
|
continue;
|
|
}
|
|
|
|
MR_snprintf(&MR_source_file_line_chars[file_name_len + 1],
|
|
LINE_NUM_MAX_CHARS, "%d", line_num);
|
|
MR_source_file_lines[MR_source_file_line_next] =
|
|
strdup(MR_source_file_line_chars);
|
|
MR_source_file_line_next++;
|
|
}
|
|
}
|
|
}
|
|
|
|
static int
|
|
MR_compare_source_file_lines(const void *ptr1, const void *ptr2)
|
|
{
|
|
char * const *sptr1 = (char * const *) ptr1;
|
|
char * const *sptr2 = (char * const *) ptr2;
|
|
return strcmp(*sptr1, *sptr2);
|
|
}
|
|
|
|
MR_CompleterList *
|
|
MR_trace_break_completer(const char *word, size_t word_len)
|
|
{
|
|
MR_CompleterList *completer_list;
|
|
MR_CompleterList *cur_completer_list;
|
|
|
|
completer_list = MR_trace_proc_spec_completer(word, word_len);
|
|
|
|
if (MR_strneq(word, "pred*", 5) || MR_strneq(word, "func*", 5)) {
|
|
// These prefixes say that the breakpoint is on a named procedure,
|
|
// not on a filename/linenumber pair.
|
|
|
|
return completer_list;
|
|
}
|
|
|
|
if (! MR_source_file_lines_initialized) {
|
|
int module_num;
|
|
int last;
|
|
int i;
|
|
|
|
for (module_num = 0; module_num < MR_module_info_next; module_num++) {
|
|
MR_insert_module_into_source_file_line_table(
|
|
MR_module_infos[module_num]);
|
|
}
|
|
|
|
qsort(MR_source_file_lines, MR_source_file_line_next,
|
|
sizeof(const char *), MR_compare_source_file_lines);
|
|
|
|
// Squeeze out any duplicate entries, which are now guaranteed
|
|
// to be adjacent to each other.
|
|
|
|
last = 0;
|
|
for (i = 1; i < MR_source_file_line_next; i++) {
|
|
if (MR_streq(MR_source_file_lines[i], MR_source_file_lines[last])) {
|
|
free((void *) MR_source_file_lines[i]);
|
|
} else {
|
|
++last;
|
|
MR_source_file_lines[last] = MR_source_file_lines[i];
|
|
}
|
|
}
|
|
|
|
// Add the NULL entry as the sentinel.
|
|
++last;
|
|
MR_source_file_lines[last] = NULL;
|
|
|
|
MR_source_file_line_next = last + 1;
|
|
MR_source_file_lines_initialized = MR_TRUE;
|
|
}
|
|
|
|
cur_completer_list = completer_list;
|
|
while (cur_completer_list->MR_completer_list_next != NULL) {
|
|
cur_completer_list = cur_completer_list->MR_completer_list_next;
|
|
}
|
|
|
|
cur_completer_list->MR_completer_list_next =
|
|
MR_trace_string_array_completer(MR_source_file_lines);
|
|
|
|
return completer_list;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
// Use Readline's filename completer.
|
|
|
|
MR_CompleterList *
|
|
MR_trace_filename_completer(const char *word, size_t word_len)
|
|
{
|
|
return MR_new_completer_elem(&MR_trace_filename_completer_next,
|
|
(MR_CompleterData) 0, MR_trace_no_free);
|
|
}
|
|
|
|
static char *
|
|
MR_trace_filename_completer_next(const char *word, size_t word_len,
|
|
MR_CompleterData *data)
|
|
{
|
|
#if !defined(MR_USE_READLINE) && !defined(MR_USE_EDITLINE)
|
|
return NULL;
|
|
#else
|
|
MR_Integer state;
|
|
|
|
state = (MR_Integer) *data;
|
|
*data = (MR_CompleterData) 1;
|
|
return rl_filename_completion_function((char *) word, (int) state);
|
|
#endif // defined(MR_USE_READLINE) || defined(MR_USE_EDITLINE)
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
// Apply a filter to the output of a completer.
|
|
|
|
typedef struct {
|
|
MR_CompleterFilter MR_filter_func;
|
|
MR_CompleterData MR_filter_data;
|
|
MR_FreeCompleterData MR_filter_free_data;
|
|
MR_CompleterList *MR_filter_list;
|
|
} MR_Filter_Completer_Data;
|
|
|
|
MR_CompleterList *
|
|
MR_trace_filter_completer(MR_CompleterFilter filter,
|
|
MR_CompleterData filter_data, MR_FreeCompleterData free_filter_data,
|
|
MR_CompleterList *list)
|
|
{
|
|
MR_Filter_Completer_Data *data;
|
|
|
|
data = MR_NEW(MR_Filter_Completer_Data);
|
|
data->MR_filter_func = filter;
|
|
data->MR_filter_data = filter_data;
|
|
data->MR_filter_free_data = free_filter_data;
|
|
data->MR_filter_list = list;
|
|
return MR_new_completer_elem(MR_trace_filter_completer_next,
|
|
(MR_CompleterData) data, MR_trace_free_filter_completer_data);
|
|
}
|
|
|
|
static char *
|
|
MR_trace_filter_completer_next(const char *word, size_t word_len,
|
|
MR_CompleterData *completer_data)
|
|
{
|
|
MR_Filter_Completer_Data *data;
|
|
char *completion;
|
|
|
|
data = (MR_Filter_Completer_Data *) *completer_data;
|
|
while (1) {
|
|
completion = MR_trace_completer_list_next(word, word_len,
|
|
&data->MR_filter_list);
|
|
if (completion == NULL) {
|
|
return NULL;
|
|
} else if (data->MR_filter_func(completion, &(data->MR_filter_data))) {
|
|
return completion;
|
|
} else {
|
|
MR_free(completion);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
MR_trace_free_filter_completer_data(MR_CompleterData completer_data)
|
|
{
|
|
MR_Filter_Completer_Data *data;
|
|
|
|
data = (MR_Filter_Completer_Data *) completer_data;
|
|
data->MR_filter_free_data(data->MR_filter_data);
|
|
MR_trace_free_completer_list(data->MR_filter_list);
|
|
MR_free(data);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
// Apply a mapping function to the output of a completer.
|
|
|
|
typedef struct {
|
|
MR_Completer_Map MR_map_func;
|
|
MR_CompleterData MR_map_data;
|
|
MR_FreeCompleterData MR_map_free_data;
|
|
MR_CompleterList *MR_map_list;
|
|
} MR_MapCompleterData;
|
|
|
|
MR_CompleterList *
|
|
MR_trace_map_completer(MR_Completer_Map map, MR_CompleterData map_data,
|
|
MR_FreeCompleterData free_data, MR_CompleterList *list)
|
|
{
|
|
MR_MapCompleterData *data;
|
|
|
|
data = MR_NEW(MR_MapCompleterData);
|
|
data->MR_map_func = map;
|
|
data->MR_map_data = map_data;
|
|
data->MR_map_free_data = free_data;
|
|
data->MR_map_list = list;
|
|
return MR_new_completer_elem(MR_trace_map_completer_next,
|
|
(MR_CompleterData) data, MR_trace_free_map_completer_data);
|
|
}
|
|
|
|
static char *
|
|
MR_trace_map_completer_next(const char *word, size_t word_len,
|
|
MR_CompleterData *completer_data)
|
|
{
|
|
MR_MapCompleterData *data;
|
|
char *completion;
|
|
|
|
data = (MR_MapCompleterData *) *completer_data;
|
|
completion = MR_trace_completer_list_next(word, word_len,
|
|
&data->MR_map_list);
|
|
if (completion == NULL) {
|
|
return NULL;
|
|
} else {
|
|
return data->MR_map_func(completion, &(data->MR_map_data));
|
|
}
|
|
}
|
|
|
|
static void
|
|
MR_trace_free_map_completer_data(MR_CompleterData completer_data)
|
|
{
|
|
MR_MapCompleterData *data;
|
|
|
|
data = (MR_MapCompleterData *) completer_data;
|
|
data->MR_map_free_data(data->MR_map_data);
|
|
MR_trace_free_completer_list(data->MR_map_list);
|
|
MR_free(data);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
MR_CompleterList *
|
|
MR_new_completer_elem(MR_Completer completer, MR_CompleterData data,
|
|
MR_FreeCompleterData free_data)
|
|
{
|
|
MR_CompleterList *result;
|
|
|
|
result = MR_NEW(MR_CompleterList);
|
|
result->MR_completer_func = completer;
|
|
result->MR_completer_func_data = data;
|
|
result->MR_free_completer_func_data = free_data;
|
|
result->MR_completer_list_next = NULL;
|
|
return result;
|
|
}
|
|
|
|
void
|
|
MR_trace_no_free(MR_CompleterData data)
|
|
{
|
|
}
|