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