9 #include <glib/gstdio.h>
10 #include <glib/gi18n-lib.h>
12 /* Internal function: ensure that an fseek() is called on a file pointer in
13 between reading and writing operations, and vice versa. This will only come up
14 for ReadWrite or WriteAppend files. */
16 ensure_file_operation(strid_t str, glui32 op)
18 if(str->lastop != 0 && str->lastop != op)
20 long pos = ftell(str->file_pointer);
22 WARNING_S("ftell() failed", g_strerror(errno));
23 if(fseek(str->file_pointer, pos, SEEK_SET) != 0)
24 WARNING_S("fseek() failed", g_strerror(errno));
26 str->lastop = op; /* Not 0, because we are about to do the operation anyway */
31 **************** WRITING FUNCTIONS ********************************************
35 /* Internal function: write a UTF-8 string to a text buffer window's text buffer. */
37 write_utf8_to_window_buffer(winid_t win, gchar *s)
39 if(win->input_request_type == INPUT_REQUEST_LINE || win->input_request_type == INPUT_REQUEST_LINE_UNICODE)
41 ILLEGAL("Tried to print to a text buffer window with line input pending.");
45 // Write to the buffer
46 g_string_append(win->buffer, s);
49 /* Internal function: flush a window's text buffer to the screen. */
51 flush_window_buffer(winid_t win)
54 g_printf("%s\n", win->buffer->str);
56 if(win->type != wintype_TextBuffer && win->type != wintype_TextGrid)
59 if(win->buffer->len == 0)
64 GtkTextBuffer *buffer = gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) );
67 case wintype_TextBuffer:
69 GtkTextIter start, end;
70 gtk_text_buffer_get_end_iter(buffer, &end);
73 start_offset = gtk_text_iter_get_offset(&end);
74 gtk_text_buffer_insert(buffer, &end, win->buffer->str, -1);
75 gtk_text_buffer_get_iter_at_offset(buffer, &start, start_offset);
76 style_apply(win, &start, &end);
78 ChimaraGlk *glk = CHIMARA_GLK(gtk_widget_get_ancestor(win->widget, CHIMARA_TYPE_GLK));
80 g_signal_emit_by_name(glk, "text-buffer-output", win->rock, win->buffer->str);
84 case wintype_TextGrid:
86 /* Number of characters to insert */
87 glong length = win->buffer->len;
88 glong chars_left = length;
90 GtkTextMark *cursor = gtk_text_buffer_get_mark(buffer, "cursor_position");
92 /* Get cursor position */
93 GtkTextIter start, insert;
96 gtk_text_buffer_get_iter_at_mark(buffer, &insert, cursor);
98 while(chars_left > 0 && !gtk_text_iter_is_end(&insert))
100 /* Spaces available on this line */
101 gint available_space = win->width - gtk_text_iter_get_line_offset(&insert);
103 GtkTextIter end = insert;
104 if(chars_left <= available_space)
105 gtk_text_iter_forward_chars(&end, chars_left);
107 gtk_text_iter_forward_to_line_end(&end);
109 gtk_text_buffer_delete(buffer, &insert, &end);
111 start_offset = gtk_text_iter_get_offset(&insert);
112 gtk_text_buffer_insert(buffer, &insert, win->buffer->str + (length - chars_left), MIN(chars_left, available_space));
113 gtk_text_buffer_get_iter_at_offset(buffer, &start, start_offset);
114 style_apply(win, &start, &insert);
116 chars_left -= available_space;
118 if(gtk_text_iter_get_line_offset(&insert) >= win->width)
119 gtk_text_iter_forward_line(&insert);
122 gtk_text_buffer_move_mark(buffer, cursor, &insert);
129 g_string_truncate(win->buffer, 0);
132 /* Internal function: write a Latin-1 buffer with length to a stream. */
134 write_buffer_to_stream(strid_t str, gchar *buf, glui32 len)
138 case STREAM_TYPE_WINDOW:
139 /* Each window type has a different way of printing to it */
140 switch(str->window->type)
142 /* Printing to these windows' streams does nothing */
145 case wintype_Graphics:
146 str->write_count += len;
149 /* Text grid/buffer windows */
150 case wintype_TextGrid:
152 gchar *utf8 = convert_latin1_to_utf8(buf, len);
154 /* Deal with newlines */
157 for(i=0; i<len; i++) {
158 if(utf8[i] == '\n') {
160 write_utf8_to_window_buffer(str->window, line);
161 flush_window_buffer(str->window);
163 /* Move cursor position forward to the next line */
165 GtkTextIter cursor_pos;
166 GtkTextView *textview = GTK_TEXT_VIEW(str->window->widget);
167 GtkTextBuffer *buffer = gtk_text_view_get_buffer(textview);
168 GtkTextMark *cursor_mark = gtk_text_buffer_get_mark(buffer, "cursor_position");
170 gtk_text_buffer_get_iter_at_mark( buffer, &cursor_pos, cursor_mark);
171 gtk_text_view_forward_display_line(textview, &cursor_pos);
172 gtk_text_view_backward_display_line_start(textview, &cursor_pos);
173 gtk_text_buffer_move_mark(buffer, cursor_mark, &cursor_pos);
176 line = utf8 + (i < len-1 ? (i+1):(len-1));
180 /* No more newlines left. */
181 write_utf8_to_window_buffer(str->window, line);
185 str->write_count += len;
189 case wintype_TextBuffer:
191 gchar *utf8 = convert_latin1_to_utf8(buf, len);
193 write_utf8_to_window_buffer(str->window, utf8);
197 str->write_count += len;
200 ILLEGAL_PARAM("Unknown window type: %u", str->window->type);
203 /* Now write the same buffer to the window's echo stream */
204 if(str->window->echo_stream != NULL)
205 write_buffer_to_stream(str->window->echo_stream, buf, len);
209 case STREAM_TYPE_MEMORY:
210 if(str->unicode && str->ubuffer)
213 while(str->mark < str->buflen && foo < len)
214 str->ubuffer[str->mark++] = (unsigned char)buf[foo++];
216 if(!str->unicode && str->buffer)
218 int copycount = MIN(len, str->buflen - str->mark);
219 memmove(str->buffer + str->mark, buf, copycount);
220 str->mark += copycount;
223 /* Move the EOF marker if we wrote past it */
224 if(str->mark > str->endmark)
225 str->endmark = str->mark;
227 str->write_count += len;
230 case STREAM_TYPE_FILE:
235 gchar *writebuffer = convert_latin1_to_ucs4be_string(buf, len);
236 ensure_file_operation(str, filemode_Write);
237 fwrite(writebuffer, sizeof(gchar), len * 4, str->file_pointer);
240 else /* Regular file */
242 ensure_file_operation(str, filemode_Write);
243 fwrite(buf, sizeof(gchar), len, str->file_pointer);
246 else /* Text mode is the same for Unicode and regular files */
248 gchar *utf8 = convert_latin1_to_utf8(buf, len);
251 ensure_file_operation(str, filemode_Write);
252 g_fprintf(str->file_pointer, "%s", utf8);
257 str->write_count += len;
259 case STREAM_TYPE_RESOURCE:
260 ILLEGAL(_("Writing to a resource stream is illegal."));
263 ILLEGAL_PARAM("Unknown stream type: %u", str->type);
267 /* Internal function: write a Unicode buffer with length to a stream. */
269 write_buffer_to_stream_uni(strid_t str, glui32 *buf, glui32 len)
273 case STREAM_TYPE_WINDOW:
274 /* Each window type has a different way of printing to it */
275 switch(str->window->type)
277 /* Printing to these windows' streams does nothing */
280 case wintype_Graphics:
281 str->write_count += len;
284 /* Text grid/buffer windows */
285 case wintype_TextGrid:
286 case wintype_TextBuffer:
288 gchar *utf8 = convert_ucs4_to_utf8(buf, len);
290 write_utf8_to_window_buffer(str->window, utf8);
294 str->write_count += len;
297 ILLEGAL_PARAM("Unknown window type: %u", str->window->type);
300 /* Now write the same buffer to the window's echo stream */
301 if(str->window->echo_stream != NULL)
302 write_buffer_to_stream_uni(str->window->echo_stream, buf, len);
306 case STREAM_TYPE_MEMORY:
307 if(str->unicode && str->ubuffer)
309 int copycount = MIN(len, str->buflen - str->mark);
310 memmove(str->ubuffer + str->mark, buf, copycount * sizeof(glui32));
311 str->mark += copycount;
313 if(!str->unicode && str->buffer)
315 gchar *latin1 = convert_ucs4_to_latin1_binary(buf, len);
316 int copycount = MIN(len, str->buflen - str->mark);
317 memmove(str->buffer + str->mark, latin1, copycount);
319 str->mark += copycount;
322 /* Move the EOF marker if we wrote past it */
323 if(str->mark > str->endmark)
324 str->endmark = str->mark;
326 str->write_count += len;
329 case STREAM_TYPE_FILE:
334 gchar *writebuffer = convert_ucs4_to_ucs4be_string(buf, len);
335 ensure_file_operation(str, filemode_Write);
336 fwrite(writebuffer, sizeof(gchar), len * 4, str->file_pointer);
339 else /* Regular file */
341 gchar *latin1 = convert_ucs4_to_latin1_binary(buf, len);
342 ensure_file_operation(str, filemode_Write);
343 fwrite(latin1, sizeof(gchar), len, str->file_pointer);
347 else /* Text mode is the same for Unicode and regular files */
349 gchar *utf8 = convert_ucs4_to_utf8(buf, len);
352 ensure_file_operation(str, filemode_Write);
353 g_fprintf(str->file_pointer, "%s", utf8);
358 str->write_count += len;
360 case STREAM_TYPE_RESOURCE:
361 ILLEGAL(_("Writing to a resource stream is illegal."));
364 ILLEGAL_PARAM("Unknown stream type: %u", str->type);
369 * glk_put_char_stream:
370 * @str: An output stream.
371 * @ch: A character in Latin-1 encoding.
373 * The same as glk_put_char(), except that you specify a stream @str to print
374 * to, instead of using the current stream. It is illegal for @str to be %NULL,
375 * or an input-only stream.
378 glk_put_char_stream(strid_t str, unsigned char ch)
380 VALID_STREAM(str, return);
381 g_return_if_fail(str->file_mode != filemode_Read);
383 write_buffer_to_stream(str, (gchar *)&ch, 1);
387 * glk_put_char_stream_uni:
388 * @str: An output stream.
389 * @ch: A Unicode code point.
391 * The same as glk_put_char_uni(), except that you specify a stream @str to
392 * print to, instead of using the current stream. It is illegal for @str to be
393 * %NULL, or an input-only stream.
396 glk_put_char_stream_uni(strid_t str, glui32 ch)
398 VALID_STREAM(str, return);
399 g_return_if_fail(str->file_mode != filemode_Read);
401 write_buffer_to_stream_uni(str, &ch, 1);
405 * glk_put_string_stream:
406 * @str: An output stream.
407 * @s: A null-terminated string in Latin-1 encoding.
409 * The same as glk_put_string(), except that you specify a stream @str to print
410 * to, instead of using the current stream. It is illegal for @str to be %NULL,
411 * or an input-only stream.
414 glk_put_string_stream(strid_t str, char *s)
416 VALID_STREAM(str, return);
420 g_return_if_fail(str->file_mode != filemode_Read);
422 write_buffer_to_stream(str, s, strlen(s));
426 * glk_put_string_stream_uni:
427 * @str: An output stream.
428 * @s: A null-terminated array of Unicode code points.
430 * The same as glk_put_string_uni(), except that you specify a stream @str to
431 * print to, instead of using the current stream. It is illegal for @str to be
432 * %NULL, or an input-only stream.
435 glk_put_string_stream_uni(strid_t str, glui32 *s)
437 VALID_STREAM(str, return);
441 g_return_if_fail(str->file_mode != filemode_Read);
443 /* An impromptu strlen() for glui32 arrays */
448 write_buffer_to_stream_uni(str, s, len);
452 * glk_put_buffer_stream:
453 * @str: An output stream.
454 * @buf: An array of characters in Latin-1 encoding.
455 * @len: Length of @buf.
457 * The same as glk_put_buffer(), except that you specify a stream @str to print
458 * to, instead of using the current stream. It is illegal for @str to be %NULL,
459 * or an input-only stream.
462 glk_put_buffer_stream(strid_t str, char *buf, glui32 len)
464 VALID_STREAM(str, return);
468 g_return_if_fail(str->file_mode != filemode_Read);
470 write_buffer_to_stream(str, buf, len);
474 * glk_put_buffer_stream_uni:
475 * @str: An output stream.
476 * @buf: An array of Unicode code points.
477 * @len: Length of @buf.
479 * The same as glk_put_buffer_uni(), except that you specify a stream @str to
480 * print to, instead of using the current stream. It is illegal for @str to be
481 * %NULL, or an input-only stream.
484 glk_put_buffer_stream_uni(strid_t str, glui32 *buf, glui32 len)
486 VALID_STREAM(str, return);
490 g_return_if_fail(str->file_mode != filemode_Read);
492 write_buffer_to_stream_uni(str, buf, len);
497 **************** READING FUNCTIONS ********************************************
501 /* Internal function: Read one big-endian four-byte character from file fp and
502 return it as a Unicode code point, or -1 on EOF */
504 read_ucs4be_char_from_file(strid_t str)
506 unsigned char readbuffer[4];
507 ensure_file_operation(str, filemode_Read);
508 if(fread(readbuffer, sizeof(unsigned char), 4, str->file_pointer) < 4)
511 readbuffer[0] << 24 |
512 readbuffer[1] << 16 |
517 /* Internal function: Read one UTF-8 character, which may be more than one byte,
518 from file fp and return it as a Unicode code point, or -1 on EOF */
520 read_utf8_char_from_file(strid_t str)
522 gchar readbuffer[4] = {0, 0, 0, 0}; /* Max UTF-8 width */
524 gunichar charresult = (gunichar)-2;
525 ensure_file_operation(str, filemode_Read);
526 for(foo = 0; foo < 4 && charresult == (gunichar)-2; foo++)
528 int ch = fgetc(str->file_pointer);
531 readbuffer[foo] = (gchar)ch;
532 charresult = g_utf8_get_char_validated(readbuffer, foo + 1);
533 /* charresult is -1 if invalid, -2 if incomplete, and the unicode code
536 /* Silently return unknown characters as 0xFFFD, Replacement Character */
537 if(charresult == (gunichar)-1 || charresult == (gunichar)-2)
542 /* Internal function: Read one UTF-8 character, which may be more than one byte,
543 from a memory stream @str, and return it as a Unicode code point. */
545 read_utf8_char_from_buffer(strid_t str)
548 gunichar charresult = (gunichar)-2;
549 char *buffer = str->buffer + str->mark;
550 size_t maxlen = str->buflen - str->mark;
555 for(foo = 1; foo <= maxlen; foo++)
557 charresult = g_utf8_get_char_validated(buffer, foo);
558 /* charresult is -1 if invalid, -2 if incomplete, and the
559 Unicode code point otherwise */
560 if(charresult != (gunichar)-2)
566 /* Return -1 on EOS */
567 if(charresult == (gunichar)-2)
569 /* Silently return unknown characters as 0xFFFD, Replacement Character */
570 if(charresult == (gunichar)-1)
575 /* Internal function: Read one big-endian four-byte character from memory and
576 return it as a Unicode code point, or -1 on EOF */
578 read_ucs4be_char_from_buffer(strid_t str)
580 glui32 ch = str->buffer[str->mark++];
581 if(str->mark >= str->buflen)
583 ch = (ch << 8) | (str->buffer[str->mark++] & 0xFF);
584 if(str->mark >= str->buflen)
586 ch = (ch << 8) | (str->buffer[str->mark++] & 0xFF);
587 if(str->mark >= str->buflen)
589 ch = (ch << 8) | (str->buffer[str->mark++] & 0xFF);
594 /* Internal function: Tell whether this code point is a Unicode newline. The
595 file pointer and eight-bit flag are included in case the newline is a CR
596 (U+000D). If the next character is LF (U+000A) then it also belongs to the
599 is_unicode_newline(glsi32 ch, strid_t str, gboolean utf8)
601 if(ch == 0x0A || ch == 0x85 || ch == 0x0C || ch == 0x2028 || ch == 0x2029)
604 glsi32 ch2 = utf8? read_utf8_char_from_file(str) :
605 read_ucs4be_char_from_file(str);
607 if(fseek(str->file_pointer, utf8? -1 : -4, SEEK_CUR) == -1)
608 WARNING_S("Seek failed on stream", g_strerror(errno) );
609 str->lastop = 0; /* can read or write after a seek */
616 /* Internal function: Read one character from a stream. Returns a value which
617 can be returned unchanged by glk_get_char_stream_uni(), but
618 glk_get_char_stream() must replace high values by the placeholder character. */
620 get_char_stream_common(strid_t str)
624 case STREAM_TYPE_RESOURCE:
627 if(!str->buffer || str->mark >= str->buflen)
630 /* Cheap big-endian stream */
631 return read_ucs4be_char_from_buffer(str);
632 /* slightly less cheap UTF8 stream */
633 return read_utf8_char_from_buffer(str);
635 /* for text streams, fall through to memory case */
636 case STREAM_TYPE_MEMORY:
639 if(!str->ubuffer || str->mark >= str->buflen)
641 glui32 ch = str->ubuffer[str->mark++];
647 if(!str->buffer || str->mark >= str->buflen)
649 unsigned char ch = str->buffer[str->mark++];
655 case STREAM_TYPE_FILE:
660 glsi32 ch = read_ucs4be_char_from_file(str);
666 else /* Regular file */
668 ensure_file_operation(str, filemode_Read);
669 int ch = fgetc(str->file_pointer);
677 else /* Text mode is the same for Unicode and regular files */
679 glsi32 ch = read_utf8_char_from_file(str);
687 ILLEGAL_PARAM("Reading illegal on stream type: %u", str->type);
693 * glk_get_char_stream:
694 * @str: An input stream.
696 * Reads one character from the stream @str. (There is no notion of a
697 * <quote>current input stream.</quote>) It is illegal for @str to be %NULL, or
698 * an output-only stream.
700 * The result will be between 0 and 255. As with all basic text functions, Glk
701 * assumes the Latin-1 encoding. See <link
702 * linkend="chimara-Character-Encoding">Character Encoding</link>. If the end
703 * of the stream has been reached, the result will be -1.
706 * Note that high-bit characters (128..255) are <emphasis>not</emphasis>
707 * returned as negative numbers.
710 * If the stream contains Unicode data — for example, if it was created
711 * with glk_stream_open_file_uni() or glk_stream_open_memory_uni() — then
712 * characters beyond 255 will be returned as 0x3F (<code>"?"</code>).
714 * It is usually more efficient to read several characters at once with
715 * glk_get_buffer_stream() or glk_get_line_stream(), as opposed to calling
716 * glk_get_char_stream() several times.
718 * Returns: A character value between 0 and 255, or -1 on end of stream.
721 glk_get_char_stream(strid_t str)
723 VALID_STREAM(str, return -1);
724 g_return_val_if_fail(str->file_mode == filemode_Read || str->file_mode == filemode_ReadWrite, -1);
726 glsi32 ch = get_char_stream_common(str);
727 return (ch > 0xFF)? PLACEHOLDER : ch;
731 * glk_get_char_stream_uni:
732 * @str: An input stream.
734 * Reads one character from the stream @str. If the end of the stream has been
735 * reached, the result will be -1.
737 * Returns: A value between 0 and 0x7FFFFFFF, or -1 on end of stream.
740 glk_get_char_stream_uni(strid_t str)
742 VALID_STREAM(str, return -1);
743 g_return_val_if_fail(str->file_mode == filemode_Read || str->file_mode == filemode_ReadWrite, -1);
745 return get_char_stream_common(str);
749 * glk_get_buffer_stream:
750 * @str: An input stream.
751 * @buf: A buffer with space for at least @len characters.
752 * @len: The number of characters to read.
754 * Reads @len characters from @str, unless the end of stream is reached first.
755 * No terminal null is placed in the buffer.
757 * Returns: The number of characters actually read.
760 glk_get_buffer_stream(strid_t str, char *buf, glui32 len)
762 VALID_STREAM(str, return 0);
763 g_return_val_if_fail(str->file_mode == filemode_Read || str->file_mode == filemode_ReadWrite, 0);
764 g_return_val_if_fail(buf != NULL, 0);
768 case STREAM_TYPE_RESOURCE:
773 while(copycount < len && str->buffer && str->mark < str->buflen)
777 ch = read_ucs4be_char_from_buffer(str);
779 ch = read_utf8_char_from_buffer(str);
780 buf[copycount++] = (ch > 0xFF)? '?' : (char)ch;
784 /* for text streams, fall through to memory case */
786 case STREAM_TYPE_MEMORY:
791 while(copycount < len && str->ubuffer && str->mark < str->buflen)
793 glui32 ch = str->ubuffer[str->mark++];
794 buf[copycount++] = (ch > 0xFF)? '?' : (char)ch;
799 if(str->buffer) /* if not, copycount stays 0 */
800 copycount = MIN(len, str->buflen - str->mark);
801 memmove(buf, str->buffer + str->mark, copycount);
802 str->mark += copycount;
805 str->read_count += copycount;
808 case STREAM_TYPE_FILE:
811 if(str->unicode) /* Binary file with 4-byte characters */
813 /* Read len characters of 4 bytes each */
814 unsigned char *readbuffer = g_new0(unsigned char, 4 * len);
815 ensure_file_operation(str, filemode_Read);
816 size_t count = fread(readbuffer, sizeof(unsigned char), 4 * len, str->file_pointer);
817 /* If there was an incomplete character */
821 WARNING("Incomplete character in binary Unicode file");
825 for(foo = 0; foo < count; foo += 4)
827 glsi32 ch = readbuffer[foo] << 24
828 | readbuffer[foo + 1] << 16
829 | readbuffer[foo + 2] << 8
830 | readbuffer[foo + 3];
831 buf[foo / 4] = (ch > 255)? 0x3F : (char)ch;
834 str->read_count += count / 4;
837 else /* Regular binary file */
839 ensure_file_operation(str, filemode_Read);
840 size_t count = fread(buf, sizeof(char), len, str->file_pointer);
841 str->read_count += count;
845 else /* Text mode is the same for Unicode and regular files */
847 /* Do it character-by-character */
849 for(foo = 0; foo < len; foo++)
851 glsi32 ch = read_utf8_char_from_file(str);
855 buf[foo] = (ch > 0xFF)? 0x3F : (gchar)ch;
860 ILLEGAL_PARAM("Reading illegal on stream type: %u", str->type);
866 * glk_get_buffer_stream_uni:
867 * @str: An input stream.
868 * @buf: A buffer with space for at least @len Unicode code points.
869 * @len: The number of characters to read.
871 * Reads @len Unicode characters from @str, unless the end of stream is reached
872 * first. No terminal null is placed in the buffer.
874 * Returns: The number of Unicode characters actually read.
877 glk_get_buffer_stream_uni(strid_t str, glui32 *buf, glui32 len)
879 VALID_STREAM(str, return 0);
880 g_return_val_if_fail(str->file_mode == filemode_Read || str->file_mode == filemode_ReadWrite, 0);
881 g_return_val_if_fail(buf != NULL, 0);
885 case STREAM_TYPE_RESOURCE:
890 while(copycount < len && str->buffer && str->mark < str->buflen)
894 ch = read_ucs4be_char_from_buffer(str);
896 ch = read_utf8_char_from_buffer(str);
897 buf[copycount++] = ch;
901 /* for text streams, fall through to memory case */
903 case STREAM_TYPE_MEMORY:
908 if(str->ubuffer) /* if not, copycount stays 0 */
909 copycount = MIN(len, str->buflen - str->mark);
910 memmove(buf, str->ubuffer + str->mark, copycount * 4);
911 str->mark += copycount;
915 while(copycount < len && str->buffer && str->mark < str->buflen)
917 unsigned char ch = str->buffer[str->mark++];
918 buf[copycount++] = ch;
922 str->read_count += copycount;
925 case STREAM_TYPE_FILE:
928 if(str->unicode) /* Binary file with 4-byte characters */
930 /* Read len characters of 4 bytes each */
931 unsigned char *readbuffer = g_new0(unsigned char, 4 * len);
932 ensure_file_operation(str, filemode_Read);
933 size_t count = fread(readbuffer, sizeof(unsigned char), 4 * len, str->file_pointer);
934 /* If there was an incomplete character */
938 WARNING("Incomplete character in binary Unicode file");
942 for(foo = 0; foo < count; foo += 4)
943 buf[foo / 4] = readbuffer[foo] << 24
944 | readbuffer[foo + 1] << 16
945 | readbuffer[foo + 2] << 8
946 | readbuffer[foo + 3];
948 str->read_count += count / 4;
951 else /* Regular binary file */
953 unsigned char *readbuffer = g_new0(unsigned char, len);
954 ensure_file_operation(str, filemode_Read);
955 size_t count = fread(readbuffer, sizeof(unsigned char), len, str->file_pointer);
957 for(foo = 0; foo < count; foo++)
958 buf[foo] = readbuffer[foo];
960 str->read_count += count;
964 else /* Text mode is the same for Unicode and regular files */
966 /* Do it character-by-character */
968 for(foo = 0; foo < len; foo++)
970 glsi32 ch = read_utf8_char_from_file(str);
979 ILLEGAL_PARAM("Reading illegal on stream type: %u", str->type);
985 * glk_get_line_stream:
986 * @str: An input stream.
987 * @buf: A buffer with space for at least @len characters.
988 * @len: The number of characters to read, plus one.
990 * Reads characters from @str, until either
992 * <alt>@len - 1</alt>
993 * <mathphrase>@len - 1</mathphrase>
995 * characters have been read or a newline has been read. It then puts a
996 * terminal null (<code>'\0'</code>) character on
997 * the end. It returns the number of characters actually read, including the
998 * newline (if there is one) but not including the terminal null.
1000 * Returns: The number of characters actually read.
1003 glk_get_line_stream(strid_t str, char *buf, glui32 len)
1005 VALID_STREAM(str, return 0);
1006 g_return_val_if_fail(str->file_mode == filemode_Read || str->file_mode == filemode_ReadWrite, 0);
1007 g_return_val_if_fail(buf != NULL, 0);
1011 case STREAM_TYPE_RESOURCE:
1016 /* Do it character-by-character */
1017 while(copycount < len - 1 && str->buffer && str->mark < str->buflen)
1021 ch = read_ucs4be_char_from_buffer(str);
1023 ch = read_utf8_char_from_buffer(str);
1024 /* Check for Unicode newline; slightly different than
1026 if(ch == 0x0A || ch == 0x85 || ch == 0x0C || ch == 0x2028 || ch == 0x2029)
1028 buf[copycount++] = '\n';
1033 if(str->buffer[str->mark] == 0x0A)
1034 str->mark++; /* skip past next newline */
1035 buf[copycount++] = '\n';
1038 buf[copycount++] = (ch > 0xFF)? '?' : (char)ch;
1040 buf[copycount] = '\0';
1043 /* for text streams, fall through to the memory case */
1045 case STREAM_TYPE_MEMORY:
1050 /* Do it character-by-character */
1051 while(copycount < len - 1 && str->ubuffer && str->mark < str->buflen)
1053 glui32 ch = str->ubuffer[str->mark++];
1054 /* Check for Unicode newline; slightly different than
1056 if(ch == 0x0A || ch == 0x85 || ch == 0x0C || ch == 0x2028 || ch == 0x2029)
1058 buf[copycount++] = '\n';
1063 if(str->ubuffer[str->mark] == 0x0A)
1064 str->mark++; /* skip past next newline */
1065 buf[copycount++] = '\n';
1068 buf[copycount++] = (ch > 0xFF)? '?' : (char)ch;
1070 buf[copycount] = '\0';
1074 if(str->buffer) /* if not, copycount stays 0 */
1075 copycount = MIN(len - 1, str->buflen - str->mark);
1076 char *endptr = memccpy(buf, str->buffer + str->mark, '\n', copycount);
1077 if(endptr) /* newline was found */
1078 copycount = endptr - buf; /* Real copy count */
1079 buf[copycount] = '\0';
1080 str->mark += copycount;
1083 str->read_count += copycount;
1086 case STREAM_TYPE_FILE:
1089 if(str->unicode) /* Binary file with 4-byte characters */
1091 /* Do it character-by-character */
1093 for(copycount = 0; copycount < len - 1; copycount++)
1095 glsi32 ch = read_ucs4be_char_from_file(str);
1098 buf[copycount] = '\0';
1102 if(is_unicode_newline(ch, str, FALSE))
1104 buf[copycount++] = '\n';
1105 buf[copycount] = '\0';
1108 buf[copycount] = (ch > 0xFF)? '?' : (char)ch;
1113 else /* Regular binary file */
1115 ensure_file_operation(str, filemode_Read);
1116 if( !fgets(buf, len, str->file_pointer) ) {
1121 int nread = strlen(buf);
1122 str->read_count += nread;
1126 else /* Text mode is the same for Unicode and regular files */
1128 /* Do it character-by-character */
1130 for(foo = 0; foo < len - 1; foo++)
1132 glsi32 ch = read_utf8_char_from_file(str);
1139 if(is_unicode_newline(ch, str, TRUE))
1142 buf[foo + 1] = '\0';
1145 buf[foo] = (ch > 0xFF)? 0x3F : (char)ch;
1151 ILLEGAL_PARAM("Reading illegal on stream type: %u", str->type);
1157 * glk_get_line_stream_uni:
1158 * @str: An input stream.
1159 * @buf: A buffer with space for at least @len Unicode code points.
1160 * @len: The number of characters to read, plus one.
1162 * Reads Unicode characters from @str, until either
1164 * <alt>@len - 1</alt>
1165 * <mathphrase>@len - 1</mathphrase>
1167 * Unicode characters have been read or a newline has been read. It then puts a
1168 * terminal null (a zero value) on the end.
1170 * Returns: The number of characters actually read, including the newline (if
1171 * there is one) but not including the terminal null.
1174 glk_get_line_stream_uni(strid_t str, glui32 *buf, glui32 len)
1176 VALID_STREAM(str, return 0);
1177 g_return_val_if_fail(str->file_mode == filemode_Read || str->file_mode == filemode_ReadWrite, 0);
1178 g_return_val_if_fail(buf != NULL, 0);
1182 case STREAM_TYPE_RESOURCE:
1187 /* Do it character-by-character */
1188 while(copycount < len - 1 && str->buffer && str->mark < str->buflen)
1192 ch = read_ucs4be_char_from_buffer(str);
1194 ch = read_utf8_char_from_buffer(str);
1195 /* Check for Unicode newline; slightly different than
1197 if(ch == 0x0A || ch == 0x85 || ch == 0x0C || ch == 0x2028 || ch == 0x2029)
1199 buf[copycount++] = '\n';
1204 if(str->ubuffer[str->mark] == 0x0A)
1205 str->mark++; /* skip past next newline */
1206 buf[copycount++] = '\n';
1209 buf[copycount++] = ch;
1211 buf[copycount] = '\0';
1214 /* for text streams, fall through to the memory case */
1216 case STREAM_TYPE_MEMORY:
1221 /* Do it character-by-character */
1222 while(copycount < len - 1 && str->ubuffer && str->mark < str->buflen)
1224 glui32 ch = str->ubuffer[str->mark++];
1225 /* Check for Unicode newline; slightly different than
1227 if(ch == 0x0A || ch == 0x85 || ch == 0x0C || ch == 0x2028 || ch == 0x2029)
1229 buf[copycount++] = '\n';
1234 if(str->ubuffer[str->mark] == 0x0A)
1235 str->mark++; /* skip past next newline */
1236 buf[copycount++] = '\n';
1239 buf[copycount++] = ch;
1241 buf[copycount] = '\0';
1245 /* No recourse to memccpy(), so do it character-by-character */
1246 while(copycount < len - 1 && str->buffer && str->mark < str->buflen)
1248 gchar ch = str->buffer[str->mark++];
1249 /* Check for newline */
1250 if(ch == '\n') /* Also check for \r and \r\n? */
1252 buf[copycount++] = '\n';
1255 buf[copycount++] = (unsigned char)ch;
1260 str->read_count += copycount;
1263 case STREAM_TYPE_FILE:
1266 if(str->unicode) /* Binary file with 4-byte characters */
1268 /* Do it character-by-character */
1270 for(copycount = 0; copycount < len - 1; copycount++)
1272 glsi32 ch = read_ucs4be_char_from_file(str);
1279 if(is_unicode_newline(ch, str, FALSE))
1281 buf[copycount++] = ch; /* Preserve newline types??? */
1285 buf[copycount] = ch;
1290 else /* Regular binary file */
1292 gchar *readbuffer = g_new0(gchar, len);
1293 ensure_file_operation(str, filemode_Read);
1294 if( !fgets(readbuffer, len, str->file_pointer) ) {
1299 glui32 count = strlen(readbuffer);
1301 for(foo = 0; foo < count + 1; foo++) /* Copy terminator */
1302 buf[foo] = (unsigned char)(readbuffer[foo]);
1303 str->read_count += count;
1307 else /* Text mode is the same for Unicode and regular files */
1309 /* Do it character-by-character */
1311 for(foo = 0; foo < len - 1; foo++)
1313 glsi32 ch = read_utf8_char_from_file(str);
1320 if(is_unicode_newline(ch, str, TRUE))
1322 buf[foo] = ch; /* Preserve newline types??? */
1332 ILLEGAL_PARAM("Reading illegal on stream type: %u", str->type);
1339 **************** SEEKING FUNCTIONS ********************************************
1344 * glk_stream_get_position:
1345 * @str: A file or memory stream.
1347 * Returns the position of the read/write mark in @str. For memory streams and
1348 * binary file streams, this is exactly the number of characters read or written
1349 * from the beginning of the stream (unless you have moved the mark with
1350 * glk_stream_set_position().) For text file streams, matters are more
1351 * ambiguous, since (for example) writing one byte to a text file may store more
1352 * than one character in the platform's native encoding. You can only be sure
1353 * that the position increases as you read or write to the file.
1355 * Additional complication: for Latin-1 memory and file streams, a character is
1356 * a byte. For Unicode memory and file streams (those created by
1357 * glk_stream_open_file_uni() and glk_stream_open_memory_uni()), a character is
1358 * a 32-bit word. So in a binary Unicode file, positions are multiples of four
1362 * If this bothers you, don't use binary Unicode files. I don't think they're
1363 * good for much anyhow.
1366 * glk_stream_get_position() on a window stream will always return zero.
1369 * It might make more sense to return the number of characters written to the
1370 * window, but existing libraries do not support this and it's not really
1371 * worth adding the feature.
1374 * Returns: position of the read/write mark in @str.
1377 glk_stream_get_position(strid_t str)
1379 VALID_STREAM(str, return 0);
1383 case STREAM_TYPE_MEMORY:
1384 case STREAM_TYPE_RESOURCE:
1386 case STREAM_TYPE_FILE:
1387 return ftell(str->file_pointer);
1388 case STREAM_TYPE_WINDOW:
1391 ILLEGAL_PARAM("Seeking illegal on stream type: %u", str->type);
1397 * glk_stream_set_position:
1398 * @str: A file or memory stream.
1399 * @pos: The position to set the mark to, relative to @seekmode.
1400 * @seekmode: One of %seekmode_Start, %seekmode_Current, or %seekmode_End.
1402 * Sets the position of the read/write mark in @str. The position is controlled
1403 * by @pos, and the meaning of @pos is controlled by @seekmode. See the
1404 * <code>seekmode_</code> constants below.
1406 * It is illegal to specify a position before the beginning or after the end of
1409 * In binary files, the mark position is exact — it corresponds with the
1410 * number of characters you have read or written. In text files, this mapping
1411 * can vary, because of linefeed conventions or other character-set
1412 * approximations. See <link linkend="chimara-Streams">Streams</link>.
1413 * glk_stream_set_position() and glk_stream_get_position() measure positions in
1414 * the platform's native encoding — after character cookery. Therefore,
1415 * in a text stream, it is safest to use glk_stream_set_position() only to move
1416 * to the beginning or end of a file, or to a position determined by
1417 * glk_stream_get_position().
1419 * Again, in Latin-1 streams, characters are bytes. In Unicode streams,
1420 * characters are 32-bit words, or four bytes each.
1422 * A window stream doesn't have a movable mark, so calling
1423 * glk_stream_set_position() has no effect.
1426 glk_stream_set_position(strid_t str, glsi32 pos, glui32 seekmode)
1428 VALID_STREAM(str, return);
1429 g_return_if_fail(!(seekmode == seekmode_Start && pos < 0));
1430 g_return_if_fail(!(seekmode == seekmode_End && pos > 0));
1434 case STREAM_TYPE_RESOURCE:
1435 case STREAM_TYPE_MEMORY:
1438 case seekmode_Start: str->mark = pos; break;
1439 case seekmode_Current: str->mark += pos; break;
1440 case seekmode_End: str->mark = str->endmark + pos; break;
1442 g_return_if_reached();
1446 case STREAM_TYPE_FILE:
1451 case seekmode_Start: whence = SEEK_SET; break;
1452 case seekmode_Current: whence = SEEK_CUR; break;
1453 case seekmode_End: whence = SEEK_END; break;
1455 g_return_if_reached();
1458 if(fseek(str->file_pointer, pos, whence) == -1)
1459 WARNING("Seek failed on file stream");
1460 str->lastop = 0; /* Either reading or writing is legal after fseek() */
1463 case STREAM_TYPE_WINDOW:
1464 break; /* Quietly do nothing */
1466 ILLEGAL_PARAM("Seeking illegal on stream type: %u", str->type);