X-Git-Url: https://git.stderr.nl/gitweb?a=blobdiff_plain;f=libchimara%2Fstream.c;h=96ab73fb08dcc97c8857541c0d91bafedbe7b33b;hb=f33fc94053b392178e9cec7cb6bd7152129863cb;hp=aa27a39cf5ca5d8047ede38f072d0eb05b048bc1;hpb=bae31a7df18ecafe05ec1a11b0961708bc3badd6;p=projects%2Fchimara%2Fchimara.git diff --git a/libchimara/stream.c b/libchimara/stream.c index aa27a39..96ab73f 100644 --- a/libchimara/stream.c +++ b/libchimara/stream.c @@ -7,34 +7,63 @@ #include #include "chimara-glk-private.h" -extern ChimaraGlkPrivate *glk_data; +extern GPrivate *glk_data_key; /* Internal function: create a stream with a specified rock value */ -static strid_t -stream_new_common(glui32 rock, glui32 fmode, enum StreamType type) +strid_t +stream_new_common(glui32 rock) { + ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key); + strid_t str = g_new0(struct glk_stream_struct, 1); str->magic = MAGIC_STREAM; str->rock = rock; - str->file_mode = fmode; - str->type = type; + if(glk_data->register_obj) + str->disprock = (*glk_data->register_obj)(str, gidisp_Class_Stream); /* Add it to the global stream list */ glk_data->stream_list = g_list_prepend(glk_data->stream_list, str); str->stream_list = glk_data->stream_list; - + return str; } -/* Internal function: create a window stream to go with window. */ -strid_t -window_stream_new(winid_t window) +/* Internal function: Stuff to do upon closing any type of stream. Call only + from Glk thread. */ +void +stream_close_common(strid_t str, stream_result_t *result) { - /* Create stream and connect it to window */ - strid_t str = stream_new_common(0, filemode_Write, STREAM_TYPE_WINDOW); - str->window = window; - str->style = "normal"; - return str; + ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key); + + if(glk_data->unregister_obj) + { + (*glk_data->unregister_obj)(str, gidisp_Class_Stream, str->disprock); + str->disprock.ptr = NULL; + } + + /* If the stream was one or more windows' echo streams, set those to NULL */ + winid_t win; + for(win = glk_window_iterate(NULL, NULL); win; + win = glk_window_iterate(win, NULL)) + if(win->echo_stream == str) + win->echo_stream = NULL; + + /* Return the character counts */ + if(result) + { + result->readcount = str->read_count; + result->writecount = str->write_count; + } + + /* Remove the stream from the global stream list */ + glk_data->stream_list = g_list_delete_link(glk_data->stream_list, str->stream_list); + + /* If it was the current output stream, set that to NULL */ + if(glk_data->current_stream == str) + glk_data->current_stream = NULL; + + str->magic = MAGIC_FREE; + g_free(str); } /** @@ -52,7 +81,8 @@ strid_t glk_stream_iterate(strid_t str, glui32 *rockptr) { VALID_STREAM_OR_NULL(str, return NULL); - + + ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key); GList *retnode; if(str == NULL) @@ -73,7 +103,8 @@ glk_stream_iterate(strid_t str, glui32 *rockptr) * @str: A stream. * * Retrieves the stream @str's rock value. See Rocks. + * linkend="chimara-Rocks">Rocks. Window streams always have rock 0; all + * other streams return whatever rock you created them with. * * Returns: A rock value. */ @@ -96,6 +127,8 @@ void glk_stream_set_current(strid_t str) { VALID_STREAM_OR_NULL(str, return); + + ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key); if(str != NULL && str->file_mode == filemode_Read) { @@ -116,6 +149,7 @@ glk_stream_set_current(strid_t str) strid_t glk_stream_get_current() { + ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key); return glk_data->current_stream; } @@ -130,6 +164,7 @@ glk_stream_get_current() void glk_put_char(unsigned char ch) { + ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key); VALID_STREAM(glk_data->current_stream, return); glk_put_char_stream(glk_data->current_stream, ch); } @@ -145,6 +180,7 @@ glk_put_char(unsigned char ch) void glk_put_char_uni(glui32 ch) { + ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key); VALID_STREAM(glk_data->current_stream, return); glk_put_char_stream_uni(glk_data->current_stream, ch); } @@ -157,13 +193,14 @@ glk_put_char_uni(glui32 ch) * equivalent to * |[ * for (ptr = s; *ptr; ptr++) - * #glk_put_char(*ptr); + * glk_put_char(*ptr); * ]| * However, it may be more efficient. */ void glk_put_string(char *s) { + ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key); VALID_STREAM(glk_data->current_stream, return); glk_put_string_stream(glk_data->current_stream, s); } @@ -179,6 +216,7 @@ glk_put_string(char *s) void glk_put_string_uni(glui32 *s) { + ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key); VALID_STREAM(glk_data->current_stream, return); glk_put_string_stream_uni(glk_data->current_stream, s); } @@ -192,13 +230,14 @@ glk_put_string_uni(glui32 *s) * to: * |[ * for (i = 0; i < len; i++) - * #glk_put_char(buf[i]); + * glk_put_char(buf[i]); * ]| * However, it may be more efficient. */ void glk_put_buffer(char *buf, glui32 len) { + ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key); VALID_STREAM(glk_data->current_stream, return); glk_put_buffer_stream(glk_data->current_stream, buf, len); } @@ -214,6 +253,7 @@ glk_put_buffer(char *buf, glui32 len) void glk_put_buffer_uni(glui32 *buf, glui32 len) { + ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key); VALID_STREAM(glk_data->current_stream, return); glk_put_buffer_stream_uni(glk_data->current_stream, buf, len); } @@ -240,11 +280,22 @@ glk_stream_open_memory(char *buf, glui32 buflen, glui32 fmode, glui32 rock) { g_return_val_if_fail(fmode != filemode_WriteAppend, NULL); - strid_t str = stream_new_common(rock, fmode, STREAM_TYPE_MEMORY); - str->buffer = buf; + strid_t str = stream_new_common(rock); + str->file_mode = fmode; + str->type = STREAM_TYPE_MEMORY; str->mark = 0; - str->buflen = buflen; + str->endmark = 0; str->unicode = FALSE; + + if(buf && buflen) + { + ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key); + str->buffer = buf; + str->buflen = buflen; + if(glk_data->register_arr) + str->buffer_rock = (*glk_data->register_arr)(buf, buflen, "&+#!Cn"); + } + return str; } @@ -268,11 +319,22 @@ glk_stream_open_memory_uni(glui32 *buf, glui32 buflen, glui32 fmode, glui32 rock { g_return_val_if_fail(fmode != filemode_WriteAppend, NULL); - strid_t str = stream_new_common(rock, fmode, STREAM_TYPE_MEMORY); - str->ubuffer = buf; + strid_t str = stream_new_common(rock); + str->file_mode = fmode; + str->type = STREAM_TYPE_MEMORY; str->mark = 0; - str->buflen = buflen; + str->endmark = 0; str->unicode = TRUE; + + if(buf && buflen) + { + ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key); + str->ubuffer = buf; + str->buflen = buflen; + if(glk_data->register_arr) + str->buffer_rock = (*glk_data->register_arr)(buf, buflen, "&+#!Iu"); + } + return str; } @@ -282,7 +344,7 @@ file_stream_new(frefid_t fileref, glui32 fmode, glui32 rock, gboolean unicode) { VALID_FILEREF(fileref, return NULL); - gchar *modestr; + const gchar *modestr; /* Binary mode is 0x000, text mode 0x100 */ gboolean binary = !(fileref->usage & fileusage_TextMode); switch(fmode) @@ -292,20 +354,23 @@ file_stream_new(frefid_t fileref, glui32 fmode, glui32 rock, gboolean unicode) ILLEGAL_PARAM("Tried to open a nonexistent file, '%s', in read mode", fileref->filename); return NULL; } - modestr = g_strdup(binary? "rb" : "r"); + modestr = binary? "rb" : "r"; break; case filemode_Write: - modestr = g_strdup(binary? "wb" : "w"); + modestr = binary? "wb" : "w"; break; case filemode_WriteAppend: - modestr = g_strdup(binary? "ab" : "a"); - break; case filemode_ReadWrite: - if( g_file_test(fileref->filename, G_FILE_TEST_EXISTS) ) { - modestr = g_strdup(binary? "r+b" : "r+"); - } else { - modestr = g_strdup(binary? "w+b" : "w+"); + { + /* We have to open the file first and then close it, in order to + both make sure it exists and be able to seek in it later */ + FILE *fp = g_fopen(fileref->filename, binary? "ab" : "a"); + if(fclose(fp) != 0) { + IO_WARNING( "Error opening file", fileref->filename, g_strerror(errno) ); + return NULL; } + modestr = binary? "r+b" : "r+"; + } break; default: ILLEGAL_PARAM("Invalid file mode: %u", fmode); @@ -313,12 +378,17 @@ file_stream_new(frefid_t fileref, glui32 fmode, glui32 rock, gboolean unicode) } FILE *fp = g_fopen(fileref->filename, modestr); - g_free(modestr); if(fp == NULL) { IO_WARNING( "Error opening file", fileref->filename, g_strerror(errno) ); return NULL; } + /* Fast-forward to the end if we are appending */ + if(fmode == filemode_WriteAppend && fseek(fp, 0, SEEK_END) != 0) { + IO_WARNING("Error fast-forwarding file to end", fileref->filename, g_strerror(errno)); + return NULL; + } + /* If they opened a file in write mode but didn't specifically get permission to do so, complain if the file already exists */ if(fileref->orig_filemode == filemode_Read && fmode != filemode_Read) { @@ -339,7 +409,9 @@ file_stream_new(frefid_t fileref, glui32 fmode, glui32 rock, gboolean unicode) } } - strid_t str = stream_new_common(rock, fmode, STREAM_TYPE_FILE); + strid_t str = stream_new_common(rock); + str->file_mode = fmode; + str->type = STREAM_TYPE_FILE; str->file_pointer = fp; str->binary = binary; str->unicode = unicode; @@ -360,9 +432,22 @@ file_stream_new(frefid_t fileref, glui32 fmode, glui32 rock, gboolean unicode) * Opens a stream which reads to or writes from a disk file. If @fmode is * %filemode_Read, the file must already exist; for the other modes, an empty * file is created if none exists. If @fmode is %filemode_Write, and the file - * already exists, it is truncated down to zero length (an empty file). If - * @fmode is %filemode_WriteAppend, the file mark is set to the end of the - * file. + * already exists, it is truncated down to zero length (an empty file); the + * other modes do not truncate. If @fmode is %filemode_WriteAppend, the file + * mark is set to the end of the file. + * + * + * Note, again, that this doesn't match stdio's fopen() call very well. See + * the file mode constants. + * + * + * If the filemode requires the file to exist, but the file does not exist, + * glk_stream_open_file() returns %NULL. + * + * The file may be read or written in text or binary mode; this is determined + * by the @fileref argument. Similarly, platform-dependent attributes such as + * file type are determined by @fileref. See File References. * * When writing in binary mode, Unicode values (characters greater than 255) * cannot be written to the file. If you try, they will be stored as 0x3F @@ -432,9 +517,18 @@ glk_stream_close(strid_t str, stream_result_t *result) return; case STREAM_TYPE_MEMORY: - /* Do nothing */ + { + ChimaraGlkPrivate *glk_data = g_private_get(glk_data_key); + if(glk_data->unregister_arr) + { + if(str->unicode) + (*glk_data->unregister_arr)(str->ubuffer, str->buflen, "&+#!Iu", str->buffer_rock); + else + (*glk_data->unregister_arr)(str->buffer, str->buflen, "&+#!Cn", str->buffer_rock); + } + } break; - + case STREAM_TYPE_FILE: if(fclose(str->file_pointer) != 0) IO_WARNING( "Failed to close file", str->filename, g_strerror(errno) ); @@ -447,32 +541,3 @@ glk_stream_close(strid_t str, stream_result_t *result) stream_close_common(str, result); } - -/* Internal function: Stuff to do upon closing any type of stream. */ -void -stream_close_common(strid_t str, stream_result_t *result) -{ - /* Remove the stream from the global stream list */ - glk_data->stream_list = g_list_delete_link(glk_data->stream_list, str->stream_list); - - /* If it was the current output stream, set that to NULL */ - if(glk_data->current_stream == str) - glk_data->current_stream = NULL; - - /* If it was one or more windows' echo streams, set those to NULL */ - winid_t win; - for(win = glk_window_iterate(NULL, NULL); win; - win = glk_window_iterate(win, NULL)) - if(win->echo_stream == str) - win->echo_stream = NULL; - - /* Return the character counts */ - if(result) - { - result->readcount = str->read_count; - result->writecount = str->write_count; - } - - str->magic = MAGIC_FREE; - g_free(str); -}