-
Notifications
You must be signed in to change notification settings - Fork 1
/
stream.c
executable file
·290 lines (240 loc) · 7.8 KB
/
stream.c
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
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
#include "glknew.h"
/* The first stream in the linked-list we keep around for
stream_iterate. */
strid_t first_stream;
strid_t glk_stream_iterate(strid_t prevstr, glui32 *rockptr) {
glui32 dummy;
printf("DEBUG: glk_stream_iterate starting with prevstr=%p\n", prevstr);
if (!rockptr) {
rockptr = &dummy;
}
strid_t candidate = first_stream;
if (prevstr == NULL) {
candidate = first_stream;
} else {
candidate = prevstr->next;
}
while (candidate && !candidate->did_dispatch_register) {
candidate = candidate->next;
}
if (!candidate) {
printf("Got to end of stream list\n");
*rockptr = 0;
return NULL;
}
printf("returning %p, ->rock=0x%x\n", candidate, candidate->rock);
*rockptr = candidate->rock;
return candidate;
}
/* A bunch of functions that simply dispatch depending on the stream
type they refer to (or, rather, depending on the vtable of the
stream).
*/
/* http://www.eblong.com/zarf/glk/glk-spec-070_5.html#s.4 */
void glk_stream_set_position(strid_t str, glsi32 pos, glui32 seekmode) {
if (str->vtable->set_position) {
(*str->vtable->set_position)(str, pos, seekmode);
} else {
printf("Attempt to set_position, but vtable entry not defined for type=%d", str->type);
exit(~0);
}
}
/* http://www.eblong.com/zarf/glk/glk-spec-070_5.html#s.4 */
glui32 glk_stream_get_position(strid_t str) {
return ((*str->vtable->get_position)(str));
}
void glk_put_char_stream_uni(strid_t str, glui32 ch) {
str->writecount++;
return ((*str->vtable->put_char_uni)(str, ch));
}
glsi32 glk_get_char_stream_uni(strid_t str) {
if (str->vtable->get_char_uni) {
str->readcount++;
return ((*str->vtable->get_char_uni)(str));
} else {
printf("Attempt to get_char_uni, but vtable entry not defined for type=%d", str->type);
exit(~0);
}
}
/* There's a concept of the "current stream" in glk. I don't like it,
it's an unneccessary global. The ills of implementing somebody
else's API. */
/* http://www.eblong.com/zarf/glk/glk-spec-070_5.html */
strid_t current_stream = NULL;
void glk_stream_set_current(strid_t str) {
current_stream = str;
}
strid_t glk_stream_get_current(void) {
return current_stream;
}
/* http://www.eblong.com/zarf/glk/glk-spec-070_5.html#s.1 */
/* There are lots of versions of putting data to a stream:
* glk_put_(char|string|buffer)(|_stream)(|_uni).
* We define:
* !stream -> stream
* !uni -> uni
* (buffer|string) -> char
* ...in order of preference. Maybe.
*/
void glk_put_char_stream(strid_t str, unsigned char ch) {
glui32 ch_uni = ch;
glk_put_char_stream_uni(current_stream, ch_uni);
}
void glk_put_string_stream(strid_t str, char *s) {
for (; *s; s++) {
glk_put_char_stream(str, (unsigned char)*s);
}
}
void glk_put_buffer_stream(strid_t str, char *buf, glui32 len) {
for (int i = 0; i < len; i++) {
glk_put_char_stream(str, buf[i]);
}
}
void glk_put_buffer_uni(glui32 *buf, glui32 len) {
glk_put_buffer_stream_uni(current_stream, buf, len);
}
void glk_put_buffer_stream_uni(strid_t str, glui32 *buf, glui32 len) {
for (int i = 0; i < len; i++) {
glk_put_char_stream_uni(str, buf[i]);
}
}
void glk_put_char_uni(glui32 ch) {
glk_put_char_stream_uni(current_stream, ch);
}
void glk_put_char(unsigned char ch) {
glk_put_char_stream(current_stream, ch);
}
void glk_put_string(char *s) {
glk_put_string_stream(current_stream, s);
}
void glk_put_buffer(char *buf, glui32 len) {
for (int i=0; i<len; i++) {
glk_put_char(buf[i]);
}
}
/* http://www.eblong.com/zarf/glk/glk-spec-070_5.html#s.2 */
/* There are several ways to read from a stream:
* glk_get_(char|buffer|line)_stream(|_uni).
*/
glsi32 glk_get_char_stream(strid_t str) {
glsi32 uni_char = glk_get_char_stream_uni(str);
/* EOF */
if (uni_char == -1)
return -1;
if (uni_char > 0xFF) {
return '?';
}
return uni_char;
}
glui32 glk_get_line_stream(strid_t str, char *buf, glui32 len) {
glui32 offset;
/* FIXME: Halt on error reading properly. */
for (offset = 0; offset < len; offset++) {
buf[offset] = glk_get_char_stream(str);
if (buf[offset] == '\n') {
break;
}
}
buf[offset+1] = '\0';
return offset;
}
glui32 glk_get_buffer_stream(strid_t str, char *buf, glui32 len) {
glui32 i;
for (i=0; i<len; i++) {
glsi32 c = glk_get_char_stream(str);
if (c == -1) {
printf("glk_get_buffer_stream terminating from -1, %d of %d\n", i, len);
return i-1;
}
if (c > 0xFF) {
printf("'?' while narrowing in glk_get_buffer_stream!\n");
exit(21);
buf[i] = '?';
} else {
buf[i] = (char)c;
/* printf("glk_get_buffer_stream [%d]: 0x%x\n", i, buf[i]); */
}
}
printf("DEBUG: glk_get_buffer_stream terminating from running out the clock, %d of %d\n", i, len);
return i;
}
strid_t glk_stream_open_base(glui32 rock, glui32 fmode, glui32 type, struct glk_stream_struct_vtable *vtable) {
struct glk_stream_struct *stream;
stream = malloc(sizeof(struct glk_stream_struct));
if (!stream) {
return stream;
}
stream->rock = rock;
stream->fmode = fmode;
stream->type = type;
stream->vtable = vtable;
stream->readcount = 0;
stream->writecount = 0;
stream->did_dispatch_register = 0;
if (dispatch_register) {
stream->dispatch_rock = dispatch_register((void *)stream, gidisp_Class_Stream);
stream->did_dispatch_register = 1;
}
if (!first_stream) {
printf("DEBUG: New stream %p is first_stream\n", stream);
first_stream = stream;
stream->next = NULL;
} else {
printf("DEBUG: New stream %p isn't first_stream\n", stream);
strid_t prev_stream = first_stream;
printf("DEBUG: Starting at prev_stream = %p\n", prev_stream);
while (prev_stream->next != NULL) {
prev_stream = prev_stream->next;
printf("DEBUG: prev_stream=%p\n", prev_stream);
}
printf("DEBUG: prev_stream=%p\n", prev_stream);
stream->next = prev_stream->next;
prev_stream->next = stream;
}
return stream;
}
/* http://www.eblong.com/zarf/glk/glk-spec-070_5.html#s.3 */
void glk_stream_close(strid_t str, stream_result_t *result) {
printf("DEBUG: glk_stream_close stream=%p\n", str);
/* We don't currently track these. */
if (result) {
result->readcount = str->readcount;
result->writecount = str->writecount;
}
/* remove stream from linked list */
if (!first_stream) {
printf("WTF, closing stream when first_stream==NULL (not even on it ourselves?)\n");
exit(32);
} else if (first_stream == str) {
printf("We were the only stream, setting first_stream to it's next\n");
first_stream = first_stream->next;
} else {
strid_t candidate = first_stream;
printf("DEBUG: In the hard case, starting with %p\n", candidate);
while (candidate->next != str) {
printf("DEBUG: in close_stream of %p, candidate=%p, next=%p\n", str, candidate, candidate->next);
candidate = candidate->next;
}
printf("DEBUG: Found ourselves after %p\n", candidate);
candidate->next = str->next;
}
/* FIXME: This probably belongs in stream_memory.c */
if (str->type == STREAM_TYPE_MEMORY && dispatch_disown) {
printf("DEBUG: (close_stream) width %d, buflen=%d\n", str->u.mem.width, str->u.mem.buflen);
if (str->u.mem.width == 4) {
dispatch_disown(str->u.mem.buf, str->u.mem.buflen, "&+#!Iu", str->u.mem.buffer_adoption_rock);
} else if (str->u.mem.width == 1) {
dispatch_disown(str->u.mem.buf, str->u.mem.buflen, "&+#!Cn", str->u.mem.buffer_adoption_rock);
} else {
printf("(close_stream) Width %d, not 1 or 4\n", str->u.mem.width);
exit(22);
}
}
/* We should probably do some sort of cleanup here, and disown the
buffer, in the case of a memory stream. */
if (dispatch_unregister) {
dispatch_unregister((void *)str, gidisp_Class_Stream, str->dispatch_rock);
}
/* Curses likes to write to a closed window stream. */
free(str);
}