Testen en debuggen van alle file en memory stream functies
[rodin/chimara.git] / src / strio.c
1 #include "stream.h"
2 #include <stdio.h>
3 #include <string.h>
4 #include <glib.h>
5 #include <glib/gstdio.h>
6
7 #define min(x,y) ( (x > y)? y : x )
8
9 /*
10  *
11  **************** WRITING FUNCTIONS ********************************************
12  *
13  */
14
15 /* Internal function: change illegal (control) characters in a string to a
16 placeholder character. Must free returned string afterwards. */
17 static gchar *
18 remove_latin1_control_characters(unsigned char *s, gsize len)
19 {
20         /* If len == 0, then return an empty string, not NULL */
21         if(len == 0)
22                 return g_strdup("");
23                         
24         gchar *retval = g_new0(gchar, len);
25         int i;
26         for(i = 0; i < len; i++)
27                 if( (s[i] < 32 && s[i] != 10) || (s[i] >= 127 && s[i] <= 159) )
28                         retval[i] = '?';
29                         /* Our placeholder character is '?'; other options are possible,
30                         like printing "0x7F" or something */
31                 else
32                         retval[i] = s[i];
33         return retval;
34 }
35
36 /* Internal function: convert a Latin-1 string to a UTF-8 string, replacing
37 Latin-1 control characters by a placeholder first. The UTF-8 string must be
38 freed afterwards. Returns NULL on error. */
39 static gchar *
40 convert_latin1_to_utf8(gchar *s, gsize len)
41 {
42         GError *error = NULL;
43         gchar *utf8;
44         gchar *canonical = remove_latin1_control_characters( (unsigned char *)s,
45                 len);
46         utf8 = g_convert(canonical, len, "UTF-8", "ISO-8859-1", NULL, NULL, &error);
47         g_free(canonical);
48         
49         if(utf8 == NULL)
50         {
51                 error_dialog(NULL, error, "Error during latin1->utf8 conversion: ");
52                 return NULL;
53         }
54         
55         return utf8;
56 }
57
58 /* Internal function: write a UTF-8 string to a window's text buffer. */
59 static void
60 write_utf8_to_window(winid_t win, gchar *s)
61 {
62         GtkTextBuffer *buffer = 
63                 gtk_text_view_get_buffer( GTK_TEXT_VIEW(win->widget) );
64
65         GtkTextIter iter;
66         gtk_text_buffer_get_end_iter(buffer, &iter);
67         gtk_text_buffer_insert(buffer, &iter, s, -1);
68 }
69
70 /* Internal function: write a UTF-8 buffer with length to a stream. */
71 static void
72 write_buffer_to_stream(strid_t str, gchar *buf, glui32 len)
73 {
74         switch(str->stream_type)
75         {
76                 case STREAM_TYPE_WINDOW:
77                         /* Each window type has a different way of printing to it */
78                         switch(str->window->window_type)
79                         {
80                                 /* Printing to these windows' streams does nothing */
81                                 case wintype_Blank:
82                                 case wintype_Pair:
83                                 case wintype_Graphics:
84                                         str->write_count += len;
85                                         break;
86                                 /* Text buffer window */        
87                                 case wintype_TextBuffer:
88                                 {
89                                         gchar *utf8 = convert_latin1_to_utf8(buf, len);
90                                         if(utf8)
91                                         {
92                                                 write_utf8_to_window(str->window, utf8);
93                                                 g_free(utf8);
94                                         }
95                                 }       
96                                         str->write_count += len;
97                                         break;
98                                 default:
99                                         g_warning("%s: Writing to this kind of window unsupported.",
100                                                 __func__);
101                         }
102                         
103                         /* Now write the same buffer to the window's echo stream */
104                         if(str->window->echo_stream != NULL)
105                                 write_buffer_to_stream(str->window->echo_stream, buf, len);
106                         
107                         break;
108                         
109                 case STREAM_TYPE_MEMORY:
110                         if(str->unicode && str->ubuffer)
111                         {
112                                 int foo = 0;
113                                 while(str->mark < str->buflen && foo < len)
114                                         str->ubuffer[str->mark++] = (unsigned char)buf[foo++];
115                         }
116                         if(!str->unicode && str->buffer)
117                         {
118                                 int copycount = min(len, str->buflen - str->mark);
119                                 memmove(str->buffer + str->mark, buf, copycount);
120                                 str->mark += copycount;
121                         }
122
123                         str->write_count += len;
124                         break;
125                         
126                 case STREAM_TYPE_FILE:
127                         if(str->binary) 
128                         {
129                                 if(str->unicode) 
130                                 {
131                                         /* Convert to four-byte big-endian */
132                                         gchar *writebuffer = g_new0(gchar, len * 4);
133                                         int i;
134                                         for(i = 0; i < len; i++)
135                                                 writebuffer[i * 4 + 3] = buf[i];
136                                         fwrite(writebuffer, sizeof(gchar), len * 4, 
137                                                 str->file_pointer);
138                                 } 
139                                 else /* Regular file */
140                                 {
141                                         fwrite(buf, sizeof(gchar), len, str->file_pointer);
142                                 }
143                         }
144                         else /* Text mode is the same for Unicode and regular files */
145                         {
146                                 gchar *utf8 = convert_latin1_to_utf8(buf, len);
147                                 g_fprintf(str->file_pointer, "%s", utf8);
148                                 g_free(utf8);
149                         }
150                         
151                         str->write_count += len;
152                         break;
153                 default:
154                         g_warning("%s: Writing to this kind of stream unsupported.",
155                                 __func__);
156         }
157 }
158
159 /**
160  * glk_put_char_stream:
161  * @str: An output stream.
162  * @ch: A character in Latin-1 encoding.
163  *
164  * Prints one character @ch to the stream @str. It is illegal for @str to be
165  * %NULL, or an input-only stream.
166  */
167 void
168 glk_put_char_stream(strid_t str, unsigned char ch)
169 {
170         g_return_if_fail(str != NULL);
171         g_return_if_fail(str->file_mode != filemode_Read);
172         
173         write_buffer_to_stream(str, (gchar *)&ch, 1);
174 }
175
176 /**
177  * glk_put_string_stream:
178  * @str: An output stream.
179  * @s: A null-terminated string in Latin-1 encoding.
180  *
181  * Prints @s to the stream @str. It is illegal for @str to be %NULL, or an
182  * input-only stream.
183  */
184 void
185 glk_put_string_stream(strid_t str, char *s)
186 {
187         g_return_if_fail(str != NULL);
188         g_return_if_fail(str->file_mode != filemode_Read);
189
190         write_buffer_to_stream(str, (gchar *)s, strlen(s));
191 }
192
193 /**
194  * glk_put_buffer_stream:
195  * @str: An output stream.
196  * @buf: An array of characters in Latin-1 encoding.
197  * @len: Length of @buf.
198  *
199  * Prints @buf to the stream @str. It is illegal for @str to be %NULL, or an
200  * input-only stream.
201  */
202 void
203 glk_put_buffer_stream(strid_t str, char *buf, glui32 len)
204 {
205         g_return_if_fail(str != NULL);
206         g_return_if_fail(str->file_mode != filemode_Read);
207         
208         write_buffer_to_stream(str, (gchar *)buf, len);
209 }
210
211 /*
212  *
213  **************** READING FUNCTIONS ********************************************
214  *
215  */
216
217 /* Internal function: Read one big-endian four-byte character from file fp and
218 return it as a Unicode code point, or -1 on EOF */
219 static glsi32
220 read_ucs4be_char_from_file(FILE *fp)
221 {
222         unsigned char readbuffer[4];
223         if(fread(readbuffer, sizeof(unsigned char), 4, fp) < 4)
224                 return -1; /* EOF */
225         return
226                 readbuffer[0] << 24 | 
227                 readbuffer[1] << 16 | 
228                 readbuffer[2] << 8  | 
229                 readbuffer[3];
230 }
231
232 /* Internal function: Read one UTF-8 character, which may be more than one byte,
233 from file fp and return it as a Unicode code point, or -1 on EOF */
234 static glsi32
235 read_utf8_char_from_file(FILE *fp)
236 {
237         gchar readbuffer[4] = {0, 0, 0, 0}; /* Max UTF-8 width */
238         int foo;
239         gunichar charresult = (gunichar)-2;
240         for(foo = 0; foo < 4 && charresult == (gunichar)-2; foo++) 
241         {
242                 int ch = fgetc(fp);
243                 if(ch == EOF)
244                         return -1;
245                 readbuffer[foo] = (gchar)ch;
246                 charresult = g_utf8_get_char_validated(readbuffer, foo + 1);
247                 /* charresult is -1 if invalid, -2 if incomplete, and the unicode code
248                 point otherwise */
249         }
250         /* Silently return unknown characters as 0xFFFD, Replacement Character */
251         if(charresult == (gunichar)-1 || charresult == (gunichar)-2) 
252                 return 0xFFFD;
253         return charresult;
254 }
255
256 /* Internal function: Tell whether this code point is a Unicode newline. The
257 file pointer and eight-bit flag are included in case the newline is a CR 
258 (U+000D). If the next character is LF (U+000A) then it also belongs to the
259 newline. */
260 static gboolean
261 is_unicode_newline(glsi32 ch, FILE *fp, gboolean utf8)
262 {
263         if(ch == 0x0A || ch == 0x85 || ch == 0x0C || ch == 0x2028 || ch == 0x2029)
264                 return TRUE;
265         if(ch == 0x0D) {
266                 glsi32 ch2 = utf8? read_utf8_char_from_file(fp) : 
267                         read_ucs4be_char_from_file(fp);
268                 if(ch2 != 0x0A)
269                         fseek(fp, utf8? -1 : -4, SEEK_CUR);
270                 return TRUE;
271         }
272         return FALSE;
273 }
274
275 /**
276  * glk_get_char_stream:
277  * @str: An input stream.
278  *
279  * Reads one character from the stream @str. (There is no notion of a ``current
280  * input stream.'') It is illegal for @str to be %NULL, or an output-only
281  * stream.
282  *
283  * The result will be between 0 and 255. As with all basic text functions, Glk
284  * assumes the Latin-1 encoding. If the end of the stream has been reached, the
285  * result will be -1. Note that high-bit characters (128..255) are
286  * <emphasis>not</emphasis> returned as negative numbers.
287  *
288  * If the stream contains Unicode data --- for example, if it was created with
289  * glk_stream_open_file_uni() or glk_stream_open_memory_uni() --- then
290  * characters beyond 255 will be returned as 0x3F ("?").
291  *
292  * Returns: A character value between 0 and 255, or -1 on end of stream.
293  */
294 glsi32
295 glk_get_char_stream(strid_t str)
296 {
297         g_return_val_if_fail(str != NULL, -1);
298         g_return_val_if_fail(str->file_mode == filemode_Read
299                 || str->file_mode == filemode_ReadWrite, -1);
300         
301         switch(str->stream_type)
302         {
303                 case STREAM_TYPE_MEMORY:
304                         if(str->unicode)
305                         {
306                                 if(!str->ubuffer || str->mark >= str->buflen)
307                                         return -1;
308                                 glui32 ch = str->ubuffer[str->mark++];
309                                 str->read_count++;
310                                 return (ch > 0xFF)? 0x3F : ch;
311                         }
312                         else
313                         {
314                                 if(!str->buffer || str->mark >= str->buflen)
315                                         return -1;
316                                 char ch = str->buffer[str->mark++];
317                                 str->read_count++;
318                                 return ch;
319                         }
320                         break;
321                         
322                 case STREAM_TYPE_FILE:
323                         if(str->binary) 
324                         {
325                                 if(str->unicode) 
326                                 {
327                                         glsi32 ch = read_ucs4be_char_from_file(str->file_pointer);
328                                         if(ch == -1)
329                                                 return -1;
330                                         str->read_count++;
331                                         return (ch > 0xFF)? 0x3F : ch;
332                                 }
333                                 else /* Regular file */
334                                 {
335                                         int ch = fgetc(str->file_pointer);
336                                         if(ch == EOF)
337                                                 return -1;
338                                         
339                                         str->read_count++;
340                                         return ch;
341                                 }
342                         }
343                         else /* Text mode is the same for Unicode and regular files */
344                         {
345                                 glsi32 ch = read_utf8_char_from_file(str->file_pointer);
346                                 if(ch == -1)
347                                         return -1;
348                                         
349                                 str->read_count++;
350                                 return (ch > 0xFF)? 0x3F : ch;
351                         }
352                 default:
353                         g_warning("%s: Reading from this kind of stream unsupported.",
354                                 __func__);
355                         return -1;
356         }
357 }
358
359 /**
360  * glk_get_buffer_stream:
361  * @str: An input stream.
362  * @buf: A buffer with space for at least @len characters.
363  * @len: The number of characters to read.
364  *
365  * Reads @len characters from @str, unless the end of stream is reached first.
366  * No terminal null is placed in the buffer.
367  *
368  * Returns: The number of characters actually read.
369  */
370 glui32
371 glk_get_buffer_stream(strid_t str, char *buf, glui32 len)
372 {
373         g_return_val_if_fail(str != NULL, 0);
374         g_return_val_if_fail(str->file_mode == filemode_Read
375                 || str->file_mode == filemode_ReadWrite, 0);
376         g_return_val_if_fail(buf != NULL, 0);
377         
378         switch(str->stream_type)
379         {
380                 case STREAM_TYPE_MEMORY:
381                 {
382                         int copycount = 0;
383                         if(str->unicode)
384                         {
385                                 while(copycount < len && str->ubuffer 
386                                         && str->mark < str->buflen) 
387                                 {
388                                         glui32 ch = str->ubuffer[str->mark++];
389                                         buf[copycount++] = (ch > 0xFF)? '?' : (char)ch;
390                                 }
391                         }
392                         else
393                         {
394                                 if(str->buffer) /* if not, copycount stays 0 */
395                                         copycount = min(len, str->buflen - str->mark);
396                                 memmove(buf, str->buffer + str->mark, copycount);
397                                 str->mark += copycount;
398                         }
399
400                         str->read_count += copycount;           
401                         return copycount;
402                 }       
403                 case STREAM_TYPE_FILE:
404                         if(str->binary) 
405                         {
406                                 if(str->unicode) /* Binary file with 4-byte characters */
407                                 {
408                                         /* Read len characters of 4 bytes each */
409                                         unsigned char *readbuffer = g_new0(unsigned char, 4 * len);
410                                         size_t count = fread(readbuffer, sizeof(unsigned char), 
411                                                 4 * len, str->file_pointer);
412                                         /* If there was an incomplete character */
413                                         if(count % 4 != 0) 
414                                         {
415                                                 count -= count % 4;
416                                                 g_warning("%s: Incomplete character in binary Unicode "
417                                                         "file.", __func__);
418                                         }
419                                         
420                                         str->read_count += count / 4;
421                                         int foo;
422                                         for(foo = 0; foo < count; foo += 4)
423                                         {
424                                                 glsi32 ch = readbuffer[foo] << 24
425                                                         | readbuffer[foo + 1] << 16
426                                                         | readbuffer[foo + 2] << 8
427                                                         | readbuffer[foo + 3];
428                                                 buf[foo / 4] = (ch > 255)? 0x3F : (char)ch;
429                                         }
430                                         g_free(readbuffer);
431                                         return count / 4;
432                                 }
433                                 else /* Regular binary file */
434                                 {
435                                         size_t count = fread(buf, sizeof(char), len, 
436                                                 str->file_pointer);
437                                         str->read_count += count;
438                                         return count;
439                                 }
440                         }
441                         else /* Text mode is the same for Unicode and regular files */
442                         {
443                                 /* Do it character-by-character */
444                                 int foo;
445                                 for(foo = 0; foo < len; foo++)
446                                 {
447                                         glsi32 ch = read_utf8_char_from_file(str->file_pointer);
448                                         if(ch == -1)
449                                                 break;
450                                         str->read_count++;
451                                         buf[foo] = (ch > 0xFF)? 0x3F : (gchar)ch;
452                                 }
453                                 return foo;
454                         }
455                 default:
456                         g_warning("%s: Reading from this kind of stream unsupported.",
457                                 __func__);
458                         return 0;
459         }
460 }
461
462 /**
463  * glk_get_line_stream:
464  * @str: An input stream.
465  * @buf: A buffer with space for at least @len characters.
466  * @len: The number of characters to read, plus one.
467  *
468  * Reads characters from @str, until either @len - 1 characters have been read
469  * or a newline has been read. It then puts a terminal null ('\0') aracter on
470  * the end. It returns the number of characters actually read, including the
471  * newline (if there is one) but not including the terminal null.
472  *
473  * It is usually more efficient to read several characters at once with
474  * glk_get_buffer_stream() or glk_get_line_stream(), as opposed to calling
475  * glk_get_char_stream() several times.
476  *
477  * Returns: The number of characters actually read.
478  */
479 glui32
480 glk_get_line_stream(strid_t str, char *buf, glui32 len)
481 {
482         g_return_val_if_fail(str != NULL, 0);
483         g_return_val_if_fail(str->file_mode == filemode_Read
484                 || str->file_mode == filemode_ReadWrite, 0);
485         g_return_val_if_fail(buf != NULL, 0);
486
487         switch(str->stream_type)
488         {
489                 case STREAM_TYPE_MEMORY:
490                 {
491                         int copycount = 0;
492                         if(str->unicode)
493                         {
494                                 /* Do it character-by-character */
495                                 while(copycount < len - 1 && str->ubuffer 
496                                         && str->mark < str->buflen) 
497                                 {
498                                         glui32 ch = str->ubuffer[str->mark++];
499                                         /* Check for Unicode newline; slightly different than
500                                         in file streams */
501                                         if(ch == 0x0A || ch == 0x85 || ch == 0x0C || ch == 0x2028 
502                                                 || ch == 0x2029)
503                                         {
504                                                 buf[copycount++] = '\n';
505                                                 break;
506                                         }
507                                         if(ch == 0x0D)
508                                         {
509                                                 if(str->ubuffer[str->mark] == 0x0A)
510                                                         str->mark++; /* skip past next newline */
511                                                 buf[copycount++] = '\n';
512                                                 break;
513                                         }
514                                         buf[copycount++] = (ch > 0xFF)? '?' : (char)ch;
515                                 }
516                                 buf[copycount] = '\0';
517                         }
518                         else
519                         {
520                                 if(str->buffer) /* if not, copycount stays 0 */
521                                         copycount = min(len - 1, str->buflen - str->mark);
522                                 char *endptr = memccpy(buf, str->buffer + str->mark, '\n',
523                                         copycount);
524                                 if(endptr) /* newline was found */
525                                         copycount = endptr - buf; /* Real copy count */
526                                 buf[copycount] = '\0';
527                                 str->mark += copycount;
528                         }
529                         
530                         str->read_count += copycount;
531                         return copycount;
532                 }       
533                 case STREAM_TYPE_FILE:
534                         if(str->binary) 
535                         {
536                                 if(str->unicode) /* Binary file with 4-byte characters */
537                                 {
538                                         /* Do it character-by-character */
539                                         int foo;
540                                         for(foo = 0; foo < len - 1; foo++)
541                                         {
542                                                 glsi32 ch = 
543                                                         read_ucs4be_char_from_file(str->file_pointer);
544                                                 if(ch == -1) 
545                                                 {
546                                                         buf[foo] = '\0';
547                                                         return foo - 1;
548                                                 }
549                                                 str->read_count++;
550                                                 if(is_unicode_newline(ch, str->file_pointer, FALSE))
551                                                 {
552                                                         buf[foo] = '\n';
553                                                         buf[foo + 1] = '\0';
554                                                         return foo;
555                                                 }
556                                                 buf[foo] = (ch > 0xFF)? '?' : (char)ch;
557                                         }
558                                         buf[len] = '\0';
559                                         return foo;
560                                 }
561                                 else /* Regular binary file */
562                                 {
563                                         fgets(buf, len, str->file_pointer);
564                                         str->read_count += strlen(buf);
565                                         return strlen(buf);
566                                 }
567                         }
568                         else /* Text mode is the same for Unicode and regular files */
569                         {
570                                 /* Do it character-by-character */
571                                 int foo;
572                                 for(foo = 0; foo < len - 1; foo++)
573                                 {
574                                         glsi32 ch = read_utf8_char_from_file(str->file_pointer);
575                                         if(ch == -1)
576                                         {
577                                                 buf[foo] = '\0';
578                                                 return foo - 1;
579                                         }
580                                         str->read_count++;
581                                         if(is_unicode_newline(ch, str->file_pointer, TRUE))
582                                         {
583                                                 buf[foo] = '\n';
584                                                 buf[foo + 1] = '\0';
585                                                 return foo;
586                                         }
587                                         buf[foo] = (ch > 0xFF)? 0x3F : (char)ch;
588                                 }
589                                 buf[len] = '\0';
590                                 return foo;
591                         }
592                 default:
593                         g_warning("%s: Reading from this kind of stream unsupported.",
594                                 __func__);
595                         return 0;
596         }
597 }
598
599 /*
600  *
601  **************** SEEKING FUNCTIONS ********************************************
602  *
603  */
604
605 /**
606  * glk_stream_get_position:
607  * @str: A file or memory stream.
608  *
609  * Returns the position of the read/write mark in @str. For memory streams and
610  * binary file streams, this is exactly the number of characters read or written
611  * from the beginning of the stream (unless you have moved the mark with
612  * glk_stream_set_position().) For text file streams, matters are more 
613  * ambiguous, since (for example) writing one byte to a text file may store more
614  * than one character in the platform's native encoding. You can only be sure
615  * that the position increases as you read or write to the file.
616  *
617  * Additional complication: for Latin-1 memory and file streams, a character is
618  * a byte. For Unicode memory and file streams (those created by
619  * glk_stream_open_file_uni() and glk_stream_open_memory_uni()), a character is
620  * a 32-bit word. So in a binary Unicode file, positions are multiples of four
621  * bytes.
622  *
623  * Returns: position of the read/write mark in @str.
624  */
625 glui32
626 glk_stream_get_position(strid_t str)
627 {
628         g_return_val_if_fail(str != NULL, 0);
629         
630         switch(str->stream_type)
631         {
632                 case STREAM_TYPE_MEMORY:
633                         return str->mark;
634                 case STREAM_TYPE_FILE:
635                         return ftell(str->file_pointer);
636                 default:
637                         g_warning("%s: Seeking not supported on this type of stream.",
638                                 __func__);
639                         return 0;
640         }
641 }
642
643 /**
644  * glk_stream_set_position:
645  * @str: A file or memory stream.
646  * @pos: The position to set the mark to, relative to @seekmode.
647  * @seekmode: One of #seekmode_Start, #seekmode_Current, or #seekmode_End.
648  *
649  * Sets the position of the read/write mark in @str. The position is controlled
650  * by @pos, and the meaning of @pos is controlled by @seekmode:
651  * <itemizedlist>
652  *  <listitem>#seekmode_Start: @pos characters after the beginning of the file.
653  *  </listitem>
654  *  <listitem>#seekmode_Current: @pos characters after the current position
655  *  (moving backwards if @pos is negative.)</listitem>
656  *  <listitem>#seekmode_End: @pos characters after the end of the file. (@pos
657  *  should always be zero or negative, so that this will move backwards to a
658  *  position within the file.</listitem>
659  * </itemizedlist>
660  * It is illegal to specify a position before the beginning or after the end of
661  * the file.
662  *
663  * In binary files, the mark position is exact --- it corresponds with the
664  * number of characters you have read or written. In text files, this mapping 
665  * can vary, because of linefeed conventions or other character-set 
666  * approximations. glk_stream_set_position() and glk_stream_get_position()
667  * measure positions in the platform's native encoding --- after character
668  * cookery. Therefore, in a text stream, it is safest to use
669  * glk_stream_set_position() only to move to the beginning or end of a file, or
670  * to a position determined by glk_stream_get_position().
671  *
672  * Again, in Latin-1 streams, characters are bytes. In Unicode streams,
673  * characters are 32-bit words, or four bytes each.
674  */
675 void
676 glk_stream_set_position(strid_t str, glsi32 pos, glui32 seekmode)
677 {
678         g_return_if_fail(str != NULL);
679         g_return_if_fail(!(seekmode == seekmode_Start && pos < 0));
680         g_return_if_fail(!(seekmode == seekmode_End || pos > 0));
681         
682         switch(str->stream_type)
683         {
684                 case STREAM_TYPE_MEMORY:
685                         switch(seekmode)
686                         {
687                                 case seekmode_Start:   str->mark = pos;  break;
688                                 case seekmode_Current: str->mark += pos; break;
689                                 case seekmode_End:     str->mark = str->buflen + pos; break;
690                                 default:
691                                         g_assert_not_reached();
692                                         return;
693                         }
694                         break;
695                 case STREAM_TYPE_FILE:
696                 {
697                         int whence;
698                         switch(seekmode)
699                         {
700                                 case seekmode_Start:   whence = SEEK_SET; break;
701                                 case seekmode_Current: whence = SEEK_CUR; break;
702                                 case seekmode_End:     whence = SEEK_END; break;
703                                 default:
704                                         g_assert_not_reached();
705                                         return;
706                         }
707                         fseek(str->file_pointer, pos, whence);
708                         break;
709                 }
710                 default:
711                         g_warning("%s: Seeking not supported on this type of stream.",
712                                 __func__);
713                         return;
714         }
715 }
716