4 #include <glib/gstdio.h>
6 #include "chimara-glk-private.h"
7 extern ChimaraGlkPrivate *glk_data;
9 /* Internal function: create a window stream to go with window. */
11 window_stream_new(winid_t window)
13 /* Create stream and connect it to window */
14 strid_t str = g_new0(struct glk_stream_struct, 1);
15 str->file_mode = filemode_Write;
16 str->type = STREAM_TYPE_WINDOW;
19 /* Add it to the global stream list */
20 glk_data->stream_list = g_list_prepend(glk_data->stream_list, str);
21 str->stream_list = glk_data->stream_list;
28 * @str: A stream, or %NULL.
29 * @rockptr: Return location for the next window's rock, or %NULL.
31 * Iterates over the list of streams; if @str is %NULL, it returns the first
32 * stream, otherwise the next stream after @str. If there are no more, it
33 * returns %NULL. The stream's rock is stored in @rockptr. If you don't want
34 * the rocks to be returned, you may set @rockptr to %NULL.
36 * The order in which streams are returned is arbitrary. The order may change
37 * every time you create or destroy a stream, invalidating the iteration.
39 * Returns: the next stream, or %NULL if there are no more.
42 glk_stream_iterate(strid_t str, glui32 *rockptr)
47 retnode = glk_data->stream_list;
49 retnode = str->stream_list->next;
50 strid_t retval = retnode? (strid_t)retnode->data : NULL;
52 /* Store the stream's rock in rockptr */
54 *rockptr = glk_stream_get_rock(retval);
60 * glk_stream_get_rock:
63 * Returns the stream @str's rock value.
65 * Returns: A rock value.
68 glk_stream_get_rock(strid_t str)
70 g_return_val_if_fail(str != NULL, 0);
75 * glk_stream_set_current:
76 * @str: An output stream, or %NULL.
78 * Sets the current stream to @str, which must be an output stream. You may set
79 * the current stream to %NULL, which means the current stream is not set to
83 glk_stream_set_current(strid_t str)
85 if(str != NULL && str->file_mode == filemode_Read)
87 g_warning("%s: Cannot set current stream to non output stream", __func__);
91 glk_data->current_stream = str;
95 * glk_stream_get_current:
97 * Returns the current stream, or %NULL if there is none.
102 glk_stream_get_current()
104 return glk_data->current_stream;
109 * @ch: A character in Latin-1 encoding.
111 * Prints one character to the current stream. As with all basic functions, the
112 * character is assumed to be in the Latin-1 character encoding.
115 glk_put_char(unsigned char ch)
117 g_return_if_fail(glk_data->current_stream != NULL);
118 glk_put_char_stream(glk_data->current_stream, ch);
123 * @ch: A Unicode code point.
125 * Prints one character to the current stream. The character is assumed to be a
126 * Unicode code point.
129 glk_put_char_uni(glui32 ch)
131 g_return_if_fail(glk_data->current_stream != NULL);
132 glk_put_char_stream_uni(glk_data->current_stream, ch);
137 * @s: A null-terminated string in Latin-1 encoding.
139 * Prints a null-terminated string to the current stream. It is exactly
141 * <informalexample><programlisting>
142 * for (ptr = s; *ptr; ptr++)
143 * glk_put_char(*ptr);
144 * </programlisting></informalexample>
145 * However, it may be more efficient.
148 glk_put_string(char *s)
150 g_return_if_fail(glk_data->current_stream != NULL);
151 glk_put_string_stream(glk_data->current_stream, s);
155 * glk_put_string_uni:
156 * @s: A zero-terminated string of Unicode code points.
158 * Prints a string of Unicode characters to the current stream. It is equivalent
159 * to a series of glk_put_char_uni() calls. A string ends on a #glui32 whose
163 glk_put_string_uni(glui32 *s)
165 g_return_if_fail(glk_data->current_stream != NULL);
166 glk_put_string_stream_uni(glk_data->current_stream, s);
171 * @buf: An array of characters in Latin-1 encoding.
172 * @len: Length of @buf.
174 * Prints a block of characters to the current stream. It is exactly equivalent
176 * <informalexample><programlisting>
177 * for (i = 0; i < len; i++)
178 * glk_put_char(buf[i]);
179 * </programlisting></informalexample>
180 * However, it may be more efficient.
183 glk_put_buffer(char *buf, glui32 len)
185 g_return_if_fail(glk_data->current_stream != NULL);
186 glk_put_buffer_stream(glk_data->current_stream, buf, len);
190 * glk_put_buffer_uni:
191 * @buf: An array of Unicode code points.
192 * @len: Length of @buf.
194 * Prints a block of Unicode characters to the current stream. It is equivalent
195 * to a series of glk_put_char_uni() calls.
198 glk_put_buffer_uni(glui32 *buf, glui32 len)
200 g_return_if_fail(glk_data->current_stream != NULL);
201 glk_put_buffer_stream_uni(glk_data->current_stream, buf, len);
205 * glk_stream_open_memory:
206 * @buf: An allocated buffer, or %NULL.
207 * @buflen: Length of @buf.
208 * @fmode: Mode in which the buffer will be opened. Must be one of
209 * #filemode_Read, #filemode_Write, or #filemode_ReadWrite.
210 * @rock: The new stream's rock value.
212 * Opens a stream which reads from or writes to a space in memory. @buf points
213 * to the buffer where output will be read from or written to. @buflen is the
214 * length of the buffer.
216 * When outputting, if more than @buflen characters are written to the stream,
217 * all of them beyond the buffer length will be thrown away, so as not to
218 * overwrite the buffer. (The character count of the stream will still be
219 * maintained correctly. That is, it will count the number of characters written
220 * into the stream, not the number that fit into the buffer.)
222 * If @buf is %NULL, or for that matter if @buflen is zero, then <emphasis>
223 * everything</emphasis> written to the stream is thrown away. This may be
224 * useful if you are interested in the character count.
226 * When inputting, if more than @buflen characters are read from the stream, the
227 * stream will start returning -1 (signalling end-of-file.) If @buf is %NULL,
228 * the stream will always return end-of-file.
230 * The data is written to the buffer exactly as it was passed to the printing
231 * functions (glk_put_char(), etc.); input functions will read the data exactly
232 * as it exists in memory. No platform-dependent cookery will be done on it.
233 * (You can write a disk file in text mode, but a memory stream is effectively
234 * always in binary mode.)
236 * Unicode values (characters greater than 255) cannot be written to the buffer.
237 * If you try, they will be stored as 0x3F ("?") characters.
239 * Whether reading or writing, the contents of the buffer are undefined until
240 * the stream is closed. The library may store the data there as it is written,
241 * or deposit it all in a lump when the stream is closed. It is illegal to
242 * change the contents of the buffer while the stream is open.
245 glk_stream_open_memory(char *buf, glui32 buflen, glui32 fmode, glui32 rock)
247 g_return_val_if_fail(fmode != filemode_WriteAppend, NULL);
249 strid_t str = g_new0(struct glk_stream_struct, 1);
251 str->file_mode = fmode;
252 str->type = STREAM_TYPE_MEMORY;
255 str->buflen = buflen;
256 str->unicode = FALSE;
258 /* Add it to the global stream list */
259 glk_data->stream_list = g_list_prepend(glk_data->stream_list, str);
260 str->stream_list = glk_data->stream_list;
266 * glk_stream_open_memory_uni:
267 * @buf: An allocated buffer, or %NULL.
268 * @buflen: Length of @buf.
269 * @fmode: Mode in which the buffer will be opened. Must be one of
270 * #filemode_Read, #filemode_Write, or #filemode_ReadWrite.
271 * @rock: The new stream's rock value.
273 * Works just like glk_stream_open_memory(), except that the buffer is an array
274 * of 32-bit words, instead of bytes. This allows you to write and read any
275 * Unicode character. The @buflen is the number of words, not the number of
279 glk_stream_open_memory_uni(glui32 *buf, glui32 buflen, glui32 fmode, glui32 rock)
281 g_return_val_if_fail(fmode != filemode_WriteAppend, NULL);
283 strid_t str = g_new0(struct glk_stream_struct, 1);
285 str->file_mode = fmode;
286 str->type = STREAM_TYPE_MEMORY;
289 str->buflen = buflen;
292 /* Add it to the global stream list */
293 glk_data->stream_list = g_list_prepend(glk_data->stream_list, str);
294 str->stream_list = glk_data->stream_list;
299 /* Internal function: create a stream using the given parameters. */
301 file_stream_new(frefid_t fileref, glui32 fmode, glui32 rock, gboolean unicode)
303 g_return_val_if_fail(fileref != NULL, NULL);
306 /* Binary mode is 0x000, text mode 0x100 */
307 gboolean binary = !(fileref->usage & fileusage_TextMode);
311 if(!g_file_test(fileref->filename, G_FILE_TEST_EXISTS)) {
312 g_warning("glk_stream_open_file: Tried to open a file in read "
313 "mode that didn't exist!");
316 modestr = g_strdup(binary? "rb" : "r");
319 modestr = g_strdup(binary? "wb" : "w");
321 case filemode_WriteAppend:
322 modestr = g_strdup(binary? "ab" : "a");
324 case filemode_ReadWrite:
325 if( g_file_test(fileref->filename, G_FILE_TEST_EXISTS) ) {
326 modestr = g_strdup(binary? "r+b" : "r+");
328 modestr = g_strdup(binary? "w+b" : "w+");
332 g_warning("glk_stream_open_file: Invalid file mode");
336 FILE *fp = g_fopen(fileref->filename, modestr);
339 g_warning("glk_stream_open_file: Error opening file");
343 /* If they opened a file in write mode but didn't specifically get
344 permission to do so, complain if the file already exists */
345 if(fileref->orig_filemode == filemode_Read && fmode != filemode_Read) {
348 GtkWidget *dialog = gtk_message_dialog_new(NULL, 0,
349 GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO,
350 "File %s already exists. Overwrite?", fileref->filename);
351 gint response = gtk_dialog_run(GTK_DIALOG(dialog));
352 gtk_widget_destroy(dialog);
356 if(response != GTK_RESPONSE_YES) {
362 strid_t str = g_new0(struct glk_stream_struct, 1);
364 str->file_mode = fmode;
365 str->type = STREAM_TYPE_FILE;
366 str->file_pointer = fp;
367 str->binary = binary;
368 str->unicode = unicode;
369 str->filename = g_filename_to_utf8(fileref->filename, -1, NULL, NULL, NULL);
370 if(str->filename == NULL)
371 str->filename = g_strdup("Unknown file name"); /* fail silently */
372 /* Add it to the global stream list */
373 glk_data->stream_list = g_list_prepend(glk_data->stream_list, str);
374 str->stream_list = glk_data->stream_list;
380 * glk_stream_open_file:
381 * @fileref: Indicates the file which will be opened.
382 * @fmode: Mode in which the file will be opened. Can be any of #filemode_Read,
383 * #filemode_Write, #filemode_WriteAppend, or #filemode_ReadWrite.
384 * @rock: The new stream's rock value.
386 * Opens a stream which reads to or writes from a disk file. If @fmode is
387 * #filemode_Read, the file must already exist; for the other modes, an empty
388 * file is created if none exists. If @fmode is #filemode_Write, and the file
389 * already exists, it is truncated down to zero length (an empty file). If
390 * @fmode is #filemode_WriteAppend, the file mark is set to the end of the
393 * The file may be written in text or binary mode; this is determined by the
394 * @fileref argument. Similarly, platform-dependent attributes such as file
395 * type are determined by @fileref.
397 * When writing in binary mode, Unicode values (characters greater than 255)
398 * cannot be written to the file. If you try, they will be stored as 0x3F ("?")
399 * characters. In text mode, Unicode values are stored in UTF-8.
401 * Returns: A new stream, or %NULL if the file operation failed.
404 glk_stream_open_file(frefid_t fileref, glui32 fmode, glui32 rock)
406 return file_stream_new(fileref, fmode, rock, FALSE);
410 * glk_stream_open_file_uni:
411 * @fileref: Indicates the file which will be opened.
412 * @fmode: Mode in which the file will be opened. Can be any of #filemode_Read,
413 * #filemode_Write, #filemode_WriteAppend, or #filemode_ReadWrite.
414 * @rock: The new stream's rock value.
416 * This works just like glk_stream_open_file(), except that in binary mode,
417 * characters are written and read as four-byte (big-endian) values. This
418 * allows you to write any Unicode character.
420 * In text mode, the file is written and read in UTF-8.
422 * Returns: A new stream, or %NULL if the file operation failed.
425 glk_stream_open_file_uni(frefid_t fileref, glui32 fmode, glui32 rock)
427 return file_stream_new(fileref, fmode, rock, TRUE);
432 * @str: Stream to close.
433 * @result: Pointer to a #stream_result_t, or %NULL.
435 * Closes the stream @str. The @result argument points to a structure which is
436 * filled in with the final character counts of the stream. If you do not care
437 * about these, you may pass %NULL as the @result argument.
439 * If @str is the current output stream, the current output stream is set to
442 * You cannot close window streams; use glk_window_close() instead.
445 glk_stream_close(strid_t str, stream_result_t *result)
447 g_return_if_fail(str != NULL);
449 /* Free resources associated with one specific type of stream */
452 case STREAM_TYPE_WINDOW:
453 g_warning("%s: Attempted to close a window stream. Use glk_window_"
454 "close() instead.", __func__);
457 case STREAM_TYPE_MEMORY:
461 case STREAM_TYPE_FILE:
462 if(fclose(str->file_pointer) != 0)
463 g_warning("%s: Failed to close file '%s'.", __func__,
465 g_free(str->filename);
468 g_warning("%s: Closing this type of stream not supported.",
473 stream_close_common(str, result);
476 /* Internal function: Stuff to do upon closing any type of stream. */
478 stream_close_common(strid_t str, stream_result_t *result)
480 /* Remove the stream from the global stream list */
481 glk_data->stream_list = g_list_delete_link(glk_data->stream_list, str->stream_list);
483 /* If it was the current output stream, set that to NULL */
484 if(glk_data->current_stream == str)
485 glk_data->current_stream = NULL;
487 /* If it was one or more windows' echo streams, set those to NULL */
489 for(win = glk_window_iterate(NULL, NULL); win;
490 win = glk_window_iterate(win, NULL))
491 if(win->echo_stream == str)
492 win->echo_stream = NULL;
494 /* Return the character counts */
497 result->readcount = str->read_count;
498 result->writecount = str->write_count;