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