File: test.c

package info (click to toggle)
libedit 3.1-20251016-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 2,768 kB
  • sloc: ansic: 16,776; sh: 4,992; awk: 427; makefile: 134
file content (169 lines) | stat: -rw-r--r-- 4,020 bytes parent folder | download | duplicates (3)
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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
#define _POSIX_C_SOURCE 200809L
#include <errno.h>
#include <fcntl.h>
#include <histedit.h>
#include <locale.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <wchar.h>

/* This function works exactly like fmemopen(), except that the memory stream
 * has an underlying file descriptor and hence can be used with libedit.
 * The mode argument is included for symmetry, but we really don't need it;
 * we always call it specified as "r" in this simple demo program.
 * As always, we return NULL with errno set on failure. */
static FILE *fmemopen_with_fd(const void *restrict bytes, size_t len, const char mode[restrict static 1]) {
	if(mode[0] != 'r' || mode[1]) {
		errno = EINVAL;
		return NULL;
	}

	int pip[2];
	if(pipe(pip) == -1) {
		return NULL;
	}

	/* We are not going to read from the pipe until we've completely written to it.
	 * Because of this, we avoid a deadlock if the pipe buffer gets full. */
	if(fcntl(pip[1], F_SETFD, O_NONBLOCK) == -1) {
closebothends:
		const int esave = errno;
		close(pip[1]);
		close(pip[0]);
		errno = esave;
		return NULL;
	}

	ssize_t k;
	size_t bytes_written = 0;
	do {
		k = write(pip[1], bytes_written + (char*)bytes, len - bytes_written);
		if(k == -1) {
			break;
		} else {
			bytes_written += k;
		}
	} while(bytes_written < len);
	if(k == -1) {
		if(errno == EAGAIN) {
			errno = ERANGE;
		}
		goto closebothends;
	}

	if(close(pip[1]) == -1) {
		const int esave = errno;
		close(pip[0]);
		errno = esave;
		return NULL;
	}

	FILE *const stream = fdopen(pip[0], "r");
	if(!stream) {
		const int esave = errno;
		close(pip[0]);
		errno = esave;
		return NULL;
	}
	return stream;
}

int main(void) {
	if(!setlocale(LC_ALL, "")) {
		fputs("Failed to enable default locale\n", stderr);
		exit(EXIT_FAILURE);
	}

	const char *const mbstest = "Test string";
	wchar_t *const wcstest = L"Test string";
	FILE *const instream = fmemopen_with_fd(mbstest, strlen(mbstest), "r");
	if(!instream) {
		perror("Failed to create stream");
		exit(EXIT_FAILURE);
	}
	EditLine *const el = el_init("test", instream, stdout, stderr);
	if(!el) {
		fputs("Failed to initialize libedit\n", stderr);
		if(fclose(instream) == EOF) {
			perror("Failed to close stream");
		}
	}

	const char *line = el_gets(el, &(int){0});
	if(!line) {
		if(feof(instream)) {
			fputs("End of file\n", stderr);
		} else {
			perror("Failed to read line");
		}
		el_end(el);
		if(fclose(instream) == EOF) {
			perror("Failed to close stream");
		}
		exit(EXIT_FAILURE);
	}
	if(fclose(instream) == EOF) {
		perror("Failed to close stream");
		el_end(el);
		exit(EXIT_FAILURE);
	}

	if(strcmp(mbstest, line)) {
		fputs("Strings did not match\n", stderr);
		abort();
	}
	el_end(el);

	/* Now let's try a wide-oriented stream.
	 * Note: We assume here that the external representation for a wide-oriented stream
	 * is the ordinary multibyte encoding. This may not be a good assumption for some locales. */
	FILE *const widestream = fmemopen_with_fd(mbstest, strlen(mbstest), "r");
	if(!widestream) {
		perror("Failed to create stream");
		exit(EXIT_FAILURE);
	}

	errno = 0;
	if(fwide(widestream, 1) <= 0) {
		if(errno) {
			perror("Failed to set wide orientation of stream");
		} else {
			fputs("Failed to set wide orientation of stream\n", stderr);
		}
		if(fclose(widestream) == EOF) {
			perror("Failed to close stream");
		}
		exit(EXIT_FAILURE);
	}

	EditLine *const elw = el_init("test", widestream, stdout, stderr);
	if(!elw) {
		fputs("Failed to initialize libedit\n", stderr);
		if(fclose(widestream) == EOF) {
			perror("Failed to close stream");
		}
		exit(EXIT_FAILURE);
	}

	const wchar_t *const wline = el_wgets(elw, &(int){0});
	if(!wline) {
		if(feof(widestream)) {
			fputs("Reached end of file\n", stderr);
		} else {
			puts("Failed to read line");
		}
		el_end(elw);
		if(fclose(widestream) == EOF) {
			perror("Failed to close stream");
		}
	}

	if(wcscmp(wline, wcstest)) {
		fputs("String comparison failed\n", stderr);
		abort();
	}

	el_end(elw);
}