Files
mercury/trace/mercury_trace_readline.c
Julien Fischer dbfa9c37ea Avoid a warning with GCC 9.1 on macOS 10.12.
trace/mercury_trace_readline.c:
    Avoid a warning about a const qualifier being discarded.
2019-09-26 00:08:50 +10:00

232 lines
6.5 KiB
C

// vim: ts=4 sw=4 expandtab ft=c
// Copyright (C) 1998-2002, 2005-2007 The University of Melbourne.
// Copyright (C) 2014-2016, 2018 The Mercury team.
// This file is distributed under the terms specified in COPYING.LIB.
// A simple interface to read a line, normally done using GNU readline.
//
// Main authors: fjh, zs.
#include "mercury_imp.h"
#include "mercury_array_macros.h"
#include "mercury_memory.h"
#include "mercury_std.h"
#include "mercury_wrapper.h"
#include "mercury_trace_readline.h"
#include "mercury_trace_completion.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 FILE *rl_instream;
extern FILE *rl_outstream;
extern char (*rl_completion_entry_function)(const char *, int);
extern const char *rl_readline_name;
extern void (*rl_prep_term_function)(int);
extern void (*rl_deprep_term_function)(void);
#endif
#ifdef MR_HAVE_READLINE_HISTORY_H
#include <readline/history.h>
#endif
#ifdef MR_HAVE_UNISTD_H
#include <unistd.h> // for isatty()
#endif
#endif
#include <stdio.h>
#include <stdlib.h>
// The initial size of the array of characters used to hold the line.
#define MR_INIT_BUF_LEN 80
static void MR_dummy_prep_term_function(int ignored);
static void MR_dummy_deprep_term_function(void);
char *
MR_trace_readline(const char *prompt, FILE *in, FILE *out)
{
#if (defined(isatty) || defined(MR_HAVE_ISATTY)) \
&& (defined(fileno) || defined(MR_HAVE_FILENO)) \
&& (defined(MR_USE_READLINE) || defined(MR_USE_EDITLINE))
char *line;
MR_bool in_isatty;
char *last_nl;
in_isatty = isatty(fileno(in));
if (in_isatty || MR_force_readline) {
rl_instream = in;
rl_outstream = out;
// The cast to (void *) silences a spurious "assignment from
// incompatible pointer type" warning (old versions of readline
// are very sloppy about declaring the types of function pointers).
rl_completion_entry_function = (void *) &MR_trace_line_completer;
rl_readline_name = (char *) "mdb";
if (!in_isatty) {
// This is necessary for tests/debugger/completion, otherwise
// we get lots of messages about readline not being able to get
// the terminal settings. This is possibly a bit flaky, but it is
// only used by our tests.
rl_prep_term_function = (void *) MR_dummy_prep_term_function;
rl_deprep_term_function = (void *) MR_dummy_deprep_term_function;
}
// If the prompt contains newlines then readline doesn't
// display it properly.
last_nl = strrchr(prompt, '\n');
if (last_nl != NULL) {
char *real_prompt;
char *pre_prompt;
real_prompt = (char *) MR_malloc(strlen(last_nl));
strcpy(real_prompt, last_nl + 1);
pre_prompt = (char *) MR_malloc(last_nl - prompt + 2);
strncpy(pre_prompt, prompt, last_nl - prompt + 1);
pre_prompt[last_nl - prompt + 1] = '\0';
fputs(pre_prompt, out);
line = readline((char *) real_prompt);
MR_free(real_prompt);
MR_free(pre_prompt);
} else {
line = readline((char *) prompt);
}
// readline() allocates with malloc(), and we want to return something
// allocated with MR_malloc(), but that's OK, because MR_malloc() and
// malloc() are interchangeable.
#if 0
{
char *tmp = line;
line = MR_copy_string(line);
free(tmp);
}
#endif
if (line != NULL && line[0] != '\0') {
add_history(line);
}
return line;
}
#endif // have isatty && have fileno && (have readline || have editline)
// Otherwise, don't use readline.
fprintf(out, "%s", prompt);
fflush(out);
return MR_trace_readline_raw(in);
}
char *
MR_trace_readline_raw(FILE *fp)
{
char *contents;
int content_max;
int c;
int i;
contents = NULL;
content_max = 0;
i = 0;
while ((c = getc(fp)) != EOF && c != '\n') {
MR_ensure_big_enough(i, content, char, MR_INIT_BUF_LEN);
contents[i++] = c;
}
if (c == '\n' || i > 0) {
MR_ensure_big_enough(i, content, char, MR_INIT_BUF_LEN);
contents[i] = '\0';
return contents;
} else {
MR_free(contents);
return NULL;
}
}
char *
MR_trace_readline_from_script(FILE *fp, char **args, int num_args)
{
char *line = NULL;
size_t line_length;
int line_index;
size_t expanded_line_length;
char *expanded_line;
int expanded_line_index;
int arg_num;
size_t arg_length;
char *arg;
do {
if (line != NULL) {
MR_free(line);
}
line = MR_trace_readline_raw(fp);
if (line == NULL) {
return NULL;
}
// Ignore lines starting with '#'.
} while (*line == '#');
line_length = strlen(line);
expanded_line_length = line_length;
expanded_line = (char*) MR_malloc(line_length + 1);
expanded_line[0] = '\0';
expanded_line_index = 0;
for (line_index = 0; line_index < line_length; line_index++) {
if ((line[line_index] == '$') &&
(line_index < (line_length - 1)) &&
(line[line_index + 1] >= '1') &&
(line[line_index + 1] <= '9'))
{
arg_num = (int)(line[line_index + 1] - '1');
if (arg_num < num_args) {
arg = args[arg_num];
arg_length = strlen(arg);
// Subtract 2 for the "$n" which will not occur
// in the expanded string.
expanded_line_length += arg_length - 2;
expanded_line = MR_realloc(expanded_line,
expanded_line_length + 1);
expanded_line[expanded_line_index] = '\0';
strcat(expanded_line, arg);
expanded_line_index += arg_length;
}
// Skip the digit after the '$'.
line_index++;
} else {
expanded_line[expanded_line_index] = line[line_index];
expanded_line_index++;
}
}
MR_free(line);
expanded_line[expanded_line_index] = '\0';
return expanded_line;
}
static void
MR_dummy_prep_term_function(int ignored)
{
}
static void
MR_dummy_deprep_term_function(void)
{
}