summaryrefslogtreecommitdiff
path: root/edit-readline.c
blob: c562c6273922748f5c59b27239b3b2f1ec48c27f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
#include "rc.h"

#include <errno.h>
#include <stdio.h>
#include <readline/readline.h>
#include <readline/history.h>
#include <readline/rltypedefs.h>

#include "edit.h"

bool editing = 1;

struct cookie {
	char *buffer;
};

/* teach readline how to quote a filename in rc */
static char *quote(char *text, int type, char *qp) {
	char *p, *r;

	/* worst case: string is entirely quote characters each of which will
	 * be doubled, plus space for initial and final quotes and \0 */
	p = r = malloc(strlen(text) * 2 + 3);
	if (!r) return 0;
	/* supply opening quote unless already there */
	if (*qp != '\'')
		*p++ = '\'';
	while (*text) {
		if (*text == '\'')
			*p++ = '\''; /* double existing quote */
		*p++ = *text++;
	}
	if (type == SINGLE_MATCH)
		*p++ = '\'';
	*p = '\0';
	return r;
}

char *unquote(char *text, int unused) {
	char *p, *r;

	p = r = malloc(strlen(text) + 1);
	if (!r) return 0;
	while (*text) {
		*p++ = *text++;
		if (*(text - 1) == '\'' && *text == '\'') ++text;
	}
	*p = '\0';
	return r;
}
static char **complete(const char *text, int start, int end) {
	//fprintf(stderr, "completer called on %s with %d and %d", text, start, end);
	/* we can look at rl_line_buffer to see whether to complete as a
	 * command or a filename (should we do variables too?). for now, assume
	 * it's a filename */
	/* it's a filename: let readline do it's default thing */
	return 0;
}

void *edit_begin(int fd) {
	List *hist;
	struct cookie *c;

	rl_catch_signals = 0;
	rl_completer_quote_characters = "'";
	rl_basic_word_break_characters = " \t\n`@$><=;|&{(";
	rl_basic_quote_characters = "";
	rl_filename_quote_characters = "\t\n !#$&'()*;<=>?@[\\]^`{|}~";
	rl_filename_quoting_function = quote;
	rl_filename_dequoting_function = unquote;
	rl_attempted_completion_function = complete;

	hist = varlookup("history");
	if (hist != NULL)
		if (read_history(hist->w) != 0 && errno != ENOENT) /* ignore if missing */
			uerror(hist->w);

	c = ealloc(sizeof *c);
	c->buffer = NULL;
	return c;
}

static void edit_catcher(int sig) {
	write(2, "\n", 1);
	rc_raise(eError);
}

static char *prompt;

char *edit_alloc(void *cookie, size_t *count) {
	struct cookie *c = cookie;
	void (*oldint)(int), (*oldquit)(int);

	oldint = sys_signal(SIGINT, edit_catcher);
	oldquit = sys_signal(SIGQUIT, edit_catcher);

	c->buffer = readline(prompt);

	sys_signal(SIGINT, oldint);
	sys_signal(SIGQUIT, oldquit);

	if (c->buffer) {
		*count = strlen(c->buffer);
		if (*count)
			add_history(c->buffer);
		c->buffer[*count] = '\n';
		++*count; /* include the \n */
	}
	return c->buffer;
}

void edit_prompt(void *cookie, char *pr) {
	prompt = pr;
}

void edit_free(void *cookie) {
	struct cookie *c = cookie;

	efree(c->buffer);
	/* Set c->buffer to NULL, allowing us to "overfree" it.  This
	   is a bit of a kludge, but it's otherwise hard to deal with
	   the case where a signal causes an early return from
	   readline. */
	c->buffer = NULL;
}

void edit_end(void *cookie) {
	struct cookie *c = cookie;

	efree(c);
}

void edit_reset(void *cookie) {
	rl_reset_terminal(NULL);
}