4 #include <glib/gstdio.h>
6 /* Global current stream */
7 static strid_t current_stream = NULL;
8 /* List of streams currently in existence */
9 static GList *stream_list = NULL;
11 /* Internal function: create a window stream to go with window. */
13 window_stream_new(winid_t window)
15 /* Create stream and connect it to window */
16 strid_t s = g_new0(struct glk_stream_struct, 1);
17 s->file_mode = filemode_Write;
18 s->stream_type = STREAM_TYPE_WINDOW;
20 /* Add it to the global stream list */
21 stream_list = g_list_prepend(stream_list, s);
22 s->stream_list = stream_list;
29 * @str: A stream, or #NULL.
30 * @rockptr: Return location for the next window's rock, or #NULL.
32 * Iterates over the list of streams; if @str is #NULL, it returns the first
33 * stream, otherwise the next stream after @str. If there are no more, it
34 * returns #NULL. The stream's rock is stored in @rockptr. If you don't want
35 * the rocks to be returned, you may set @rockptr to #NULL.
37 * The order in which streams are returned is arbitrary. The order may change
38 * every time you create or destroy a stream, invalidating the iteration.
40 * Returns: the next stream, or #NULL if there are no more.
43 glk_stream_iterate(strid_t str, glui32 *rockptr)
48 retnode = stream_list;
50 retnode = str->stream_list->next;
51 strid_t retval = retnode? (strid_t)retnode->data : NULL;
53 /* Store the stream's rock in rockptr */
55 *rockptr = glk_stream_get_rock(retval);
61 * glk_stream_get_rock:
64 * Returns the stream @str's rock value.
66 * Returns: A rock value.
69 glk_stream_get_rock(strid_t str)
71 g_return_val_if_fail(str != NULL, 0);
76 * glk_stream_set_current:
77 * @str: An output stream, or NULL.
79 * Sets the current stream to @str, or to nothing if @str is #NULL.
82 glk_stream_set_current(strid_t str)
84 if(str != NULL && str->file_mode == filemode_Read)
86 g_warning("glk_stream_set_current: "
87 "Cannot set current stream to non output stream");
95 * glk_stream_get_current:
97 * Returns the current stream, or #NULL if there is none.
102 glk_stream_get_current()
104 return current_stream;
109 * @ch: A character in Latin-1 encoding.
111 * Prints one character @ch to the current stream.
114 glk_put_char(unsigned char ch)
116 /* Illegal to print to the current stream if it is NULL */
117 g_return_if_fail(current_stream != NULL);
118 glk_put_char_stream(current_stream, ch);
123 * @s: A null-terminated string in Latin-1 encoding.
125 * Prints @s to the current stream.
128 glk_put_string(char *s)
130 /* Illegal to print to the current stream if it is NULL */
131 g_return_if_fail(current_stream != NULL);
132 glk_put_string_stream(current_stream, s);
137 * @buf: An array of characters in Latin-1 encoding.
138 * @len: Length of @buf.
140 * Prints @buf to the current stream.
143 glk_put_buffer(char *buf, glui32 len)
145 /* Illegal to print to the current stream if it is NULL */
146 g_return_if_fail(current_stream != NULL);
147 glk_put_buffer_stream(current_stream, buf, len);
151 * glk_stream_open_memory:
152 * @buf: An allocated buffer, or %NULL.
153 * @buflen: Length of @buf.
154 * @fmode: Mode in which the buffer will be opened. Must be one of
155 * #filemode_Read, #filemode_Write, or #filemode_ReadWrite.
156 * @rock: The new stream's rock value.
158 * Opens a stream which reads from or writes to a space in memory. @buf points
159 * to the buffer where output will be read from or written to. @buflen is the
160 * length of the buffer.
162 * When outputting, if more than @buflen characters are written to the stream,
163 * all of them beyond the buffer length will be thrown away, so as not to
164 * overwrite the buffer. (The character count of the stream will still be
165 * maintained correctly. That is, it will count the number of characters written
166 * into the stream, not the number that fit into the buffer.)
168 * If @buf is %NULL, or for that matter if @buflen is zero, then <emphasis>
169 * everything</emphasis> written to the stream is thrown away. This may be
170 * useful if you are interested in the character count.
172 * When inputting, if more than @buflen characters are read from the stream, the
173 * stream will start returning -1 (signalling end-of-file.) If @buf is %NULL,
174 * the stream will always return end-of-file.
176 * The data is written to the buffer exactly as it was passed to the printing
177 * functions (glk_put_char(), etc.); input functions will read the data exactly
178 * as it exists in memory. No platform-dependent cookery will be done on it.
179 * [You can write a disk file in text mode, but a memory stream is effectively
180 * always in binary mode.]
182 * Unicode values (characters greater than 255) cannot be written to the buffer.
183 * If you try, they will be stored as 0x3F ("?") characters.
185 * Whether reading or writing, the contents of the buffer are undefined until
186 * the stream is closed. The library may store the data there as it is written,
187 * or deposit it all in a lump when the stream is closed. It is illegal to
188 * change the contents of the buffer while the stream is open.
191 glk_stream_open_memory(char *buf, glui32 buflen, glui32 fmode, glui32 rock)
193 g_return_val_if_fail(fmode != filemode_WriteAppend, NULL);
195 strid_t s = g_new0(struct glk_stream_struct, 1);
197 s->file_mode = fmode;
198 s->stream_type = STREAM_TYPE_MEMORY;
204 /* Add it to the global stream list */
205 stream_list = g_list_prepend(stream_list, s);
206 s->stream_list = stream_list;
212 * glk_stream_open_memory_uni:
213 * @buf: An allocated buffer, or %NULL.
214 * @buflen: Length of @buf.
215 * @fmode: Mode in which the buffer will be opened. Must be one of
216 * #filemode_Read, #filemode_Write, or #filemode_ReadWrite.
217 * @rock: The new stream's rock value.
219 * Works just like glk_stream_open_memory(), except that the buffer is an array
220 * of 32-bit words, instead of bytes. This allows you to write and read any
221 * Unicode character. The @buflen is the number of words, not the number of
225 glk_stream_open_memory_uni(glui32 *buf, glui32 buflen, glui32 fmode,
228 g_return_val_if_fail(fmode != filemode_WriteAppend, NULL);
230 strid_t s = g_new0(struct glk_stream_struct, 1);
232 s->file_mode = fmode;
233 s->stream_type = STREAM_TYPE_MEMORY;
239 /* Add it to the global stream list */
240 stream_list = g_list_prepend(stream_list, s);
241 s->stream_list = stream_list;
246 /* Internal function: create a stream using the given parameters. */
248 file_stream_new(frefid_t fileref, glui32 fmode, glui32 rock, gboolean unicode)
250 g_return_val_if_fail(fileref != NULL, NULL);
253 /* Binary mode is 0x000, text mode 0x100 */
254 gboolean binary = !(fileref->usage & fileusage_TextMode);
258 if(!g_file_test(fileref->filename, G_FILE_TEST_EXISTS)) {
259 g_warning("glk_stream_open_file: Tried to open a file in read "
260 "mode that didn't exist!");
263 modestr = g_strdup(binary? "rb" : "r");
266 modestr = g_strdup(binary? "wb" : "w");
268 case filemode_WriteAppend:
269 modestr = g_strdup(binary? "ab" : "a");
271 case filemode_ReadWrite:
272 if( g_file_test(fileref->filename, G_FILE_TEST_EXISTS) ) {
273 modestr = g_strdup(binary? "r+b" : "r+");
275 modestr = g_strdup(binary? "w+b" : "w+");
279 g_warning("glk_stream_open_file: Invalid file mode");
283 FILE *fp = g_fopen(fileref->filename, modestr);
286 g_warning("glk_stream_open_file: Error opening file");
290 /* If they opened a file in write mode but didn't specifically get
291 permission to do so, complain if the file already exists */
292 if(fileref->orig_filemode == filemode_Read && fmode != filemode_Read) {
295 GtkWidget *dialog = gtk_message_dialog_new(NULL, 0,
296 GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO,
297 "File %s already exists. Overwrite?", fileref->filename);
298 gint response = gtk_dialog_run(GTK_DIALOG(dialog));
299 gtk_widget_destroy(dialog);
303 if(response != GTK_RESPONSE_YES) {
309 strid_t s = g_new0(struct glk_stream_struct, 1);
311 s->file_mode = fmode;
312 s->stream_type = STREAM_TYPE_FILE;
313 s->file_pointer = fp;
315 s->unicode = unicode;
316 s->filename = g_filename_to_utf8(fileref->filename, -1, NULL, NULL, NULL);
317 if(s->filename == NULL)
318 s->filename = g_strdup("Unknown file name"); /* fail silently */
319 /* Add it to the global stream list */
320 stream_list = g_list_prepend(stream_list, s);
321 s->stream_list = stream_list;
327 * glk_stream_open_file:
328 * @fileref: Indicates the file which will be opened.
329 * @fmode: Mode in which the file will be opened. Can be any of #filemode_Read,
330 * #filemode_Write, #filemode_WriteAppend, or #filemode_ReadWrite.
331 * @rock: The new stream's rock value.
333 * Opens a stream which reads to or writes from a disk file. If @fmode is
334 * #filemode_Read, the file must already exist; for the other modes, an empty
335 * file is created if none exists. If @fmode is #filemode_Write, and the file
336 * already exists, it is truncated down to zero length (an empty file). If
337 * @fmode is #filemode_WriteAppend, the file mark is set to the end of the
340 * The file may be written in text or binary mode; this is determined by the
341 * @fileref argument. Similarly, platform-dependent attributes such as file
342 * type are determined by @fileref.
344 * When writing in binary mode, Unicode values (characters greater than 255)
345 * cannot be written to the file. If you try, they will be stored as 0x3F ("?")
346 * characters. In text mode, Unicode values are stored in UTF-8.
348 * Returns: A new stream, or %NULL if the file operation failed.
351 glk_stream_open_file(frefid_t fileref, glui32 fmode, glui32 rock)
353 return file_stream_new(fileref, fmode, rock, FALSE);
357 * glk_stream_open_file_uni:
358 * @fileref: Indicates the file which will be opened.
359 * @fmode: Mode in which the file will be opened. Can be any of #filemode_Read,
360 * #filemode_Write, #filemode_WriteAppend, or #filemode_ReadWrite.
361 * @rock: The new stream's rock value.
363 * This works just like glk_stream_open_file(), except that in binary mode,
364 * characters are written and read as four-byte (big-endian) values. This
365 * allows you to write any Unicode character.
367 * In text mode, the file is written and read in UTF-8.
369 * Returns: A new stream, or %NULL if the file operation failed.
372 glk_stream_open_file_uni(frefid_t fileref, glui32 fmode, glui32 rock)
374 return file_stream_new(fileref, fmode, rock, TRUE);
379 * @str: Stream to close.
380 * @result: Pointer to a #stream_result_t, or %NULL.
382 * Closes the stream @str. The @result argument points to a structure which is
383 * filled in with the final character counts of the stream. If you do not care
384 * about these, you may pass %NULL as the @result argument.
386 * If @str is the current output stream, the current output stream is set to
389 * You cannot close window streams; use glk_window_close() instead.
392 glk_stream_close(strid_t str, stream_result_t *result)
394 g_return_if_fail(str != NULL);
396 /* Free resources associated with one specific type of stream */
397 switch(str->stream_type)
399 case STREAM_TYPE_WINDOW:
400 g_warning("%s: Attempted to close a window stream. Use glk_window_"
401 "close() instead.", __func__);
404 case STREAM_TYPE_MEMORY:
408 case STREAM_TYPE_FILE:
409 if(fclose(str->file_pointer) != 0)
410 g_warning("%s: Failed to close file '%s'.", __func__,
412 g_free(str->filename);
415 g_warning("%s: Closing this type of stream not supported.",
420 stream_close_common(str, result);
424 stream_close_common(strid_t str, stream_result_t *result)
426 /* Remove the stream from the global stream list */
427 stream_list = g_list_delete_link(stream_list, str->stream_list);
428 /* If it was the current output stream, set that to NULL */
429 if(current_stream == str)
430 current_stream = NULL;
431 /* If it was one or more windows' echo streams, set those to NULL */
433 for(win = glk_window_iterate(NULL, NULL); win;
434 win = glk_window_iterate(win, NULL))
435 if(win->echo_stream == str)
436 win->echo_stream = NULL;
437 /* Return the character counts */
440 result->readcount = str->read_count;
441 result->writecount = str->write_count;