+ ILLEGAL_PARAM("Reading illegal on stream type: %u", str->type);
+ return 0;
+ }
+}
+
+/**
+ * glk_get_line_stream_uni:
+ * @str: An input stream.
+ * @buf: A buffer with space for at least @len Unicode code points.
+ * @len: The number of characters to read, plus one.
+ *
+ * Reads Unicode characters from @str, until either
+ * <inlineequation>
+ * <alt>@len - 1</alt>
+ * <mathphrase>@len - 1</mathphrase>
+ * </inlineequation>
+ * Unicode characters have been read or a newline has been read. It then puts a
+ * terminal null (a zero value) on the end.
+ *
+ * Returns: The number of characters actually read, including the newline (if
+ * there is one) but not including the terminal null.
+ */
+glui32
+glk_get_line_stream_uni(strid_t str, glui32 *buf, glui32 len)
+{
+ VALID_STREAM(str, return 0);
+ g_return_val_if_fail(str->file_mode == filemode_Read || str->file_mode == filemode_ReadWrite, 0);
+ g_return_val_if_fail(buf != NULL, 0);
+
+ switch(str->type)
+ {
+ case STREAM_TYPE_MEMORY:
+ {
+ int copycount = 0;
+ if(str->unicode)
+ {
+ /* Do it character-by-character */
+ while(copycount < len - 1 && str->ubuffer && str->mark < str->buflen)
+ {
+ glui32 ch = str->ubuffer[str->mark++];
+ /* Check for Unicode newline; slightly different than
+ in file streams */
+ if(ch == 0x0A || ch == 0x85 || ch == 0x0C || ch == 0x2028 || ch == 0x2029)
+ {
+ buf[copycount++] = '\n';
+ break;
+ }
+ if(ch == 0x0D)
+ {
+ if(str->ubuffer[str->mark] == 0x0A)
+ str->mark++; /* skip past next newline */
+ buf[copycount++] = '\n';
+ break;
+ }
+ buf[copycount++] = ch;
+ }
+ buf[copycount] = '\0';
+ }
+ else
+ {
+ /* No recourse to memccpy(), so do it character-by-character */
+ while(copycount < len - 1 && str->buffer && str->mark < str->buflen)
+ {
+ gchar ch = str->buffer[str->mark++];
+ /* Check for newline */
+ if(ch == '\n') /* Also check for \r and \r\n? */
+ {
+ buf[copycount++] = '\n';
+ break;
+ }
+ buf[copycount++] = (unsigned char)ch;
+ }
+ buf[copycount] = 0;
+ }
+
+ str->read_count += copycount;
+ return copycount;
+ }
+ case STREAM_TYPE_FILE:
+ if(str->binary)
+ {
+ if(str->unicode) /* Binary file with 4-byte characters */
+ {
+ /* Do it character-by-character */
+ int foo;
+ for(foo = 0; foo < len - 1; foo++)
+ {
+ glsi32 ch = read_ucs4be_char_from_file(str->file_pointer);
+ if(ch == -1)
+ {
+ buf[foo] = 0;
+ return foo - 1;
+ }
+ str->read_count++;
+ if(is_unicode_newline(ch, str->file_pointer, FALSE))
+ {
+ buf[foo] = ch; /* Preserve newline types??? */
+ buf[foo + 1] = 0;
+ return foo;
+ }
+ buf[foo] = ch;
+ }
+ buf[len] = 0;
+ return foo;
+ }
+ else /* Regular binary file */
+ {
+ gchar *readbuffer = g_new0(gchar, len);
+ fgets(readbuffer, len, str->file_pointer);
+ glui32 count = strlen(readbuffer) + 1; /* Copy terminator */
+ int foo;
+ for(foo = 0; foo < count; foo++)
+ buf[foo] = (unsigned char)(readbuffer[foo]);
+ str->read_count += count;
+ return count;
+ }
+ }
+ else /* Text mode is the same for Unicode and regular files */
+ {
+ /* Do it character-by-character */
+ int foo;
+ for(foo = 0; foo < len - 1; foo++)
+ {
+ glsi32 ch = read_utf8_char_from_file(str->file_pointer);
+ if(ch == -1)
+ {
+ buf[foo] = 0;
+ return foo - 1;
+ }
+ str->read_count++;
+ if(is_unicode_newline(ch, str->file_pointer, TRUE))
+ {
+ buf[foo] = ch; /* Preserve newline types??? */
+ buf[foo + 1] = 0;
+ return foo;
+ }
+ buf[foo] = ch;
+ }
+ buf[len] = 0;
+ return foo;
+ }
+ default:
+ ILLEGAL_PARAM("Reading illegal on stream type: %u", str->type);