1 /*****************************************************************************
3 *****************************************************************************
4 * Copyright (C) 1999-2004 VLC authors and VideoLAN
5 * Copyright 2008-2015 RĂ©mi Denis-Courmont
7 * Authors: Laurent Aimar <fenrir@via.ecp.fr>
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU Lesser General Public License as published by
11 * the Free Software Foundation; either version 2.1 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public License
20 * along with this program; if not, write to the Free Software Foundation,
21 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 *****************************************************************************/
35 #include <vlc_common.h>
36 #include <vlc_block.h>
37 #include <vlc_access.h>
38 #include <vlc_charset.h>
39 #include <vlc_interrupt.h>
40 #include <vlc_stream_extractor.h>
44 #include "mrl_helpers.h"
46 typedef struct stream_priv_t
49 void (*destroy
)(stream_t
*);
55 /* UTF-16 and UTF-32 file reading */
58 unsigned char char_width
;
62 max_align_t private_data
[];
66 * Allocates a VLC stream object
68 stream_t
*vlc_stream_CustomNew(vlc_object_t
*parent
,
69 void (*destroy
)(stream_t
*), size_t size
,
70 const char *type_name
)
72 stream_priv_t
*priv
= vlc_custom_create(parent
, sizeof (*priv
) + size
,
74 if (unlikely(priv
== NULL
))
77 stream_t
*s
= &priv
->stream
;
87 s
->p_input_item
= NULL
;
88 assert(destroy
!= NULL
);
89 priv
->destroy
= destroy
;
95 /* UTF16 and UTF32 text file conversion */
96 priv
->text
.conv
= (vlc_iconv_t
)(-1);
97 priv
->text
.char_width
= 1;
98 priv
->text
.little_endian
= false;
103 void *vlc_stream_Private(stream_t
*stream
)
105 return ((stream_priv_t
*)stream
)->private_data
;
108 stream_t
*vlc_stream_CommonNew(vlc_object_t
*parent
,
109 void (*destroy
)(stream_t
*))
111 return vlc_stream_CustomNew(parent
, destroy
, 0, "stream");
114 void stream_CommonDelete(stream_t
*s
)
116 stream_priv_t
*priv
= (stream_priv_t
*)s
;
118 if (priv
->text
.conv
!= (vlc_iconv_t
)(-1))
119 vlc_iconv_close(priv
->text
.conv
);
121 if (priv
->peek
!= NULL
)
122 block_Release(priv
->peek
);
123 if (priv
->block
!= NULL
)
124 block_Release(priv
->block
);
127 vlc_object_delete(s
);
133 void vlc_stream_Delete(stream_t
*s
)
135 stream_priv_t
*priv
= (stream_priv_t
*)s
;
138 stream_CommonDelete(s
);
141 stream_t
*(vlc_stream_NewURL
)(vlc_object_t
*p_parent
, const char *psz_url
)
146 stream_t
*s
= stream_AccessNew( p_parent
, NULL
, NULL
, false, psz_url
);
148 msg_Err( p_parent
, "no suitable access module for `%s'", psz_url
);
150 s
= stream_FilterAutoNew(s
);
154 stream_t
*(vlc_stream_NewMRL
)(vlc_object_t
* parent
, const char* mrl
)
156 stream_t
* stream
= vlc_stream_NewURL( parent
, mrl
);
161 char const* anchor
= strchr( mrl
, '#' );
167 if( stream_extractor_AttachParsed( &stream
, anchor
+ 1, &extra
) )
169 msg_Err( parent
, "unable to open %s", mrl
);
170 vlc_stream_Delete( stream
);
174 if( extra
&& *extra
)
175 msg_Warn( parent
, "ignoring extra fragment data: %s", extra
);
181 * Read from the stream until first newline.
182 * \param s Stream handle to read from
183 * \return A pointer to the allocated output string. You need to free this when you are done.
185 #define STREAM_PROBE_LINE 2048
186 #define STREAM_LINE_MAX (2048*100)
187 char *vlc_stream_ReadLine( stream_t
*s
)
189 stream_priv_t
*priv
= (stream_priv_t
*)s
;
191 /* Let's fail quickly if this is a readdir access */
192 if( s
->pf_read
== NULL
&& s
->pf_block
== NULL
)
196 if( vlc_stream_Tell( s
) == 0 )
198 const uint8_t *p_data
;
199 ssize_t i_data
= vlc_stream_Peek( s
, &p_data
, 2 );
204 if( unlikely(priv
->text
.conv
!= (vlc_iconv_t
)-1) )
205 { /* seek back to beginning? reset */
206 vlc_iconv_close( priv
->text
.conv
);
207 priv
->text
.conv
= (vlc_iconv_t
)-1;
209 priv
->text
.char_width
= 1;
210 priv
->text
.little_endian
= false;
214 const char *psz_encoding
= NULL
;
215 bool little_endian
= false;
217 if( !memcmp( p_data
, "\xFF\xFE", 2 ) )
219 psz_encoding
= "UTF-16LE";
220 little_endian
= true;
222 else if( !memcmp( p_data
, "\xFE\xFF", 2 ) )
224 psz_encoding
= "UTF-16BE";
227 /* Open the converter if we need it */
228 if( psz_encoding
!= NULL
)
230 msg_Dbg( s
, "UTF-16 BOM detected" );
231 priv
->text
.conv
= vlc_iconv_open( "UTF-8", psz_encoding
);
232 if( unlikely(priv
->text
.conv
== (vlc_iconv_t
)-1) )
234 msg_Err( s
, "iconv_open failed" );
237 priv
->text
.char_width
= 2;
238 priv
->text
.little_endian
= little_endian
;
244 const uint8_t *p_data
;
248 size_t i_peek
= i_line
== 0 ? STREAM_PROBE_LINE
249 : __MIN( i_line
* 2, STREAM_LINE_MAX
);
251 /* Probe more data */
252 ssize_t i_data
= vlc_stream_Peek( s
, &p_data
, i_peek
);
256 /* Deal here with lone-byte incomplete UTF-16 sequences at EOF
257 that we won't be able to process anyway */
258 if( i_data
< priv
->text
.char_width
)
260 assert( priv
->text
.char_width
== 2 );
262 ssize_t i_inc
= vlc_stream_Read( s
, &inc
, priv
->text
.char_width
);
263 assert( i_inc
== i_data
);
265 msg_Err( s
, "discarding incomplete UTF-16 sequence at EOF: 0x%02x", inc
);
269 /* Keep to text encoding character width boundary */
270 if( i_data
% priv
->text
.char_width
)
271 i_data
= i_data
- ( i_data
% priv
->text
.char_width
);
273 if( (size_t) i_data
== i_line
)
274 break; /* No more data */
276 assert( (size_t) i_data
> i_line
);
278 /* Resume search for an EOL where we left off */
279 const uint8_t *p_cur
= p_data
+ i_line
, *psz_eol
;
281 /* FIXME: <CR> behavior varies depending on where buffer
282 boundaries happen to fall; a <CR><LF> across the boundary
283 creates a bogus empty line. */
284 if( priv
->text
.char_width
== 1 )
287 psz_eol
= memchr( p_cur
, '\n', i_data
- i_line
);
288 if( psz_eol
== NULL
)
290 psz_eol
= memchr( p_cur
, '\r', i_data
- i_line
);
294 const uint8_t *p_last
= p_data
+ i_data
- priv
->text
.char_width
;
295 uint16_t eol
= priv
->text
.little_endian
? 0x0A00 : 0x000A;
297 assert( priv
->text
.char_width
== 2 );
299 /* UTF-16: 000A <LF> */
300 for( const uint8_t *p
= p_cur
; p
<= p_last
; p
+= 2 )
302 if( U16_AT( p
) == eol
)
309 if( psz_eol
== NULL
)
310 { /* UTF-16: 000D <CR> */
311 eol
= priv
->text
.little_endian
? 0x0D00 : 0x000D;
312 for( const uint8_t *p
= p_cur
; p
<= p_last
; p
+= 2 )
314 if( U16_AT( p
) == eol
)
325 i_line
= (psz_eol
- p_data
) + priv
->text
.char_width
;
326 /* We have our line */
332 if( i_line
>= STREAM_LINE_MAX
)
334 msg_Err( s
, "line too long, exceeding %zu bytes",
335 (size_t) STREAM_LINE_MAX
);
340 if( i_line
== 0 ) /* We failed to read any data, probably EOF */
343 /* If encoding conversion is required, UTF-8 needs at most 150%
344 as long a buffer as UTF-16 */
345 size_t i_line_conv
= priv
->text
.char_width
== 1 ? i_line
: i_line
* 3 / 2;
346 char *p_line
= malloc( i_line_conv
+ 1 ); /* +1 for easy \0 append */
349 void *p_read
= p_line
;
351 if( priv
->text
.char_width
> 1 )
353 size_t i_in
= i_line
, i_out
= i_line_conv
;
354 const char * p_in
= (char *) p_data
;
355 char * p_out
= p_line
;
357 if( vlc_iconv( priv
->text
.conv
, &p_in
, &i_in
, &p_out
, &i_out
) == VLC_ICONV_ERR
)
359 msg_Err( s
, "conversion error: %s", vlc_strerror_c( errno
) );
360 msg_Dbg( s
, "original: %zu, in %zu, out %zu", i_line
, i_in
, i_out
);
362 size_t r
= vlc_iconv( priv
->text
.conv
, NULL
, NULL
, NULL
, NULL
);
364 /* FIXME: the rest of the line is discarded and lost */
367 i_line_conv
-= i_out
;
368 p_read
= NULL
; /* Line already read, only need to advance the stream */
371 ssize_t i_data
= vlc_stream_Read( s
, p_read
, i_line
);
372 assert( i_data
> 0 && (size_t) i_data
== i_line
);
380 i_line
= i_line_conv
;
382 /* Remove trailing LF/CR */
383 while( i_line
>= 1 &&
384 (p_line
[i_line
- 1] == '\r' || p_line
[i_line
- 1] == '\n') )
387 /* Make sure the \0 is there */
388 p_line
[i_line
] = '\0';
393 static ssize_t
vlc_stream_CopyBlock(block_t
**restrict pp
,
394 void *buf
, size_t len
)
396 block_t
*block
= *pp
;
401 if (len
> block
->i_buffer
)
402 len
= block
->i_buffer
;
405 memcpy(buf
, block
->p_buffer
, len
);
407 block
->p_buffer
+= len
;
408 block
->i_buffer
-= len
;
410 if (block
->i_buffer
== 0)
412 block_Release(block
);
416 return likely(len
> 0) ? (ssize_t
)len
: -1;
419 static ssize_t
vlc_stream_ReadRaw(stream_t
*s
, void *buf
, size_t len
)
421 stream_priv_t
*priv
= (stream_priv_t
*)s
;
424 assert(len
<= SSIZE_MAX
);
429 if (s
->pf_read
!= NULL
)
431 assert(priv
->block
== NULL
);
434 if (unlikely(len
== 0))
438 ret
= s
->pf_read(s
, dummy
, len
<= 256 ? len
: 256);
441 ret
= s
->pf_read(s
, buf
, len
);
445 ret
= vlc_stream_CopyBlock(&priv
->block
, buf
, len
);
449 if (s
->pf_block
!= NULL
)
453 priv
->block
= s
->pf_block(s
, &eof
);
454 ret
= vlc_stream_CopyBlock(&priv
->block
, buf
, len
);
463 ssize_t
vlc_stream_ReadPartial(stream_t
*s
, void *buf
, size_t len
)
465 stream_priv_t
*priv
= (stream_priv_t
*)s
;
468 ret
= vlc_stream_CopyBlock(&priv
->peek
, buf
, len
);
472 assert(ret
<= (ssize_t
)len
);
476 ret
= vlc_stream_ReadRaw(s
, buf
, len
);
480 priv
->eof
= len
!= 0;
481 assert(ret
<= (ssize_t
)len
);
485 ssize_t
vlc_stream_Read(stream_t
*s
, void *buf
, size_t len
)
491 ssize_t ret
= vlc_stream_ReadPartial(s
, buf
, len
);
498 buf
= (char *)buf
+ ret
;
499 assert(len
>= (size_t)ret
);
507 ssize_t
vlc_stream_Peek(stream_t
*s
, const uint8_t **restrict bufp
, size_t len
)
509 stream_priv_t
*priv
= (stream_priv_t
*)s
;
522 peek
= block_Alloc(len
);
523 if (unlikely(peek
== NULL
))
529 if (peek
->i_buffer
< len
)
531 size_t avail
= peek
->i_buffer
;
533 peek
= block_TryRealloc(peek
, 0, len
);
534 if (unlikely(peek
== NULL
))
537 peek
->i_buffer
= avail
;
541 *bufp
= peek
->p_buffer
;
543 while (peek
->i_buffer
< len
)
545 size_t avail
= peek
->i_buffer
;
548 ret
= vlc_stream_ReadRaw(s
, peek
->p_buffer
+ avail
, len
- avail
);
552 peek
->i_buffer
+= ret
;
555 return peek
->i_buffer
;
561 block_t
*vlc_stream_ReadBlock(stream_t
*s
)
563 stream_priv_t
*priv
= (stream_priv_t
*)s
;
572 if (priv
->peek
!= NULL
)
577 else if (priv
->block
!= NULL
)
582 else if (s
->pf_block
!= NULL
)
585 block
= s
->pf_block(s
, &priv
->eof
);
589 block
= block_Alloc(4096);
590 if (unlikely(block
== NULL
))
593 ssize_t ret
= s
->pf_read(s
, block
->p_buffer
, block
->i_buffer
);
595 block
->i_buffer
= ret
;
598 block_Release(block
);
606 priv
->offset
+= block
->i_buffer
;
611 uint64_t vlc_stream_Tell(const stream_t
*s
)
613 const stream_priv_t
*priv
= (const stream_priv_t
*)s
;
618 bool vlc_stream_Eof(const stream_t
*s
)
620 const stream_priv_t
*priv
= (const stream_priv_t
*)s
;
625 int vlc_stream_Seek(stream_t
*s
, uint64_t offset
)
627 stream_priv_t
*priv
= (stream_priv_t
*)s
;
631 block_t
*peek
= priv
->peek
;
634 if (offset
>= priv
->offset
635 && offset
<= (priv
->offset
+ peek
->i_buffer
))
636 { /* Seeking within the peek buffer */
637 size_t fwd
= offset
- priv
->offset
;
639 peek
->p_buffer
+= fwd
;
640 peek
->i_buffer
-= fwd
;
641 priv
->offset
= offset
;
643 if (peek
->i_buffer
== 0)
654 if (priv
->offset
== offset
)
655 return VLC_SUCCESS
; /* Nothing to do! */
658 if (s
->pf_seek
== NULL
)
661 int ret
= s
->pf_seek(s
, offset
);
662 if (ret
!= VLC_SUCCESS
)
665 priv
->offset
= offset
;
673 if (priv
->block
!= NULL
)
675 block_Release(priv
->block
);
683 * Use to control the "stream_t *". Look at #stream_query_e for
684 * possible "i_query" value and format arguments. Return VLC_SUCCESS
685 * if ... succeed ;) and VLC_EGENERIC if failed or unimplemented
687 int vlc_stream_vaControl(stream_t
*s
, int cmd
, va_list args
)
689 stream_priv_t
*priv
= (stream_priv_t
*)s
;
693 case STREAM_SET_TITLE
:
694 case STREAM_SET_SEEKPOINT
:
696 int ret
= s
->pf_control(s
, cmd
, args
);
697 if (ret
!= VLC_SUCCESS
)
702 if (priv
->peek
!= NULL
)
704 block_Release(priv
->peek
);
708 if (priv
->block
!= NULL
)
710 block_Release(priv
->block
);
717 return s
->pf_control(s
, cmd
, args
);
721 * Read data into a block.
723 * @param s stream to read data from
724 * @param size number of bytes to read
725 * @return a block of data, or NULL on error
726 @ note The block size may be shorter than requested if the end-of-stream was
729 block_t
*vlc_stream_Block( stream_t
*s
, size_t size
)
731 if( unlikely(size
> SSIZE_MAX
) )
734 block_t
*block
= block_Alloc( size
);
735 if( unlikely(block
== NULL
) )
738 ssize_t val
= vlc_stream_Read( s
, block
->p_buffer
, size
);
741 block_Release( block
);
745 block
->i_buffer
= val
;
749 int vlc_stream_ReadDir( stream_t
*s
, input_item_node_t
*p_node
)
751 assert(s
->pf_readdir
!= NULL
);
752 return s
->pf_readdir( s
, p_node
);