1 /*****************************************************************************
3 *****************************************************************************
4 * Copyright (C) 1999-2004 VLC authors and VideoLAN
5 * Copyright 2008-2015 RĂ©mi Denis-Courmont
8 * Authors: Laurent Aimar <fenrir@via.ecp.fr>
10 * This program is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU Lesser General Public License as published by
12 * the Free Software Foundation; either version 2.1 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public License
21 * along with this program; if not, write to the Free Software Foundation,
22 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23 *****************************************************************************/
35 #include <vlc_common.h>
36 #include <vlc_block.h>
37 #include <vlc_memory.h>
38 #include <vlc_access.h>
39 #include <vlc_charset.h>
40 #include <vlc_interrupt.h>
41 #include <vlc_stream_extractor.h>
45 #include "mrl_helpers.h"
47 typedef struct stream_priv_t
50 void (*destroy
)(stream_t
*);
56 /* UTF-16 and UTF-32 file reading */
59 unsigned char char_width
;
65 * Allocates a VLC stream object
67 stream_t
*vlc_stream_CommonNew(vlc_object_t
*parent
,
68 void (*destroy
)(stream_t
*))
70 stream_priv_t
*priv
= vlc_custom_create(parent
, sizeof (*priv
), "stream");
71 if (unlikely(priv
== NULL
))
74 stream_t
*s
= &priv
->stream
;
86 assert(destroy
!= NULL
);
87 priv
->destroy
= destroy
;
93 /* UTF16 and UTF32 text file conversion */
94 priv
->text
.conv
= (vlc_iconv_t
)(-1);
95 priv
->text
.char_width
= 1;
96 priv
->text
.little_endian
= false;
101 void stream_CommonDelete(stream_t
*s
)
103 stream_priv_t
*priv
= (stream_priv_t
*)s
;
105 if (priv
->text
.conv
!= (vlc_iconv_t
)(-1))
106 vlc_iconv_close(priv
->text
.conv
);
108 if (priv
->peek
!= NULL
)
109 block_Release(priv
->peek
);
110 if (priv
->block
!= NULL
)
111 block_Release(priv
->block
);
114 vlc_object_release(s
);
120 void vlc_stream_Delete(stream_t
*s
)
122 stream_priv_t
*priv
= (stream_priv_t
*)s
;
125 stream_CommonDelete(s
);
128 stream_t
*(vlc_stream_NewURL
)(vlc_object_t
*p_parent
, const char *psz_url
)
133 stream_t
*s
= stream_AccessNew( p_parent
, NULL
, false, psz_url
);
135 msg_Err( p_parent
, "no suitable access module for `%s'", psz_url
);
139 stream_t
*(vlc_stream_NewMRL
)(vlc_object_t
* parent
, const char* mrl
)
141 stream_t
* stream
= vlc_stream_NewURL( parent
, mrl
);
146 char const* anchor
= strchr( mrl
, '#' );
152 if( stream_extractor_AttachParsed( &stream
, anchor
+ 1, &extra
) )
154 msg_Err( parent
, "unable to open %s", mrl
);
155 vlc_stream_Delete( stream
);
159 if( extra
&& *extra
)
160 msg_Warn( parent
, "ignoring extra fragment data: %s", extra
);
166 * Read from the stream until first newline.
167 * \param s Stream handle to read from
168 * \return A pointer to the allocated output string. You need to free this when you are done.
170 #define STREAM_PROBE_LINE 2048
171 #define STREAM_LINE_MAX (2048*100)
172 char *vlc_stream_ReadLine( stream_t
*s
)
174 stream_priv_t
*priv
= (stream_priv_t
*)s
;
176 int i_line
= 0, i_read
= 0;
178 /* Let's fail quickly if this is a readdir access */
179 if( s
->pf_read
== NULL
&& s
->pf_block
== NULL
)
185 const uint8_t *p_data
;
190 i_data
= vlc_stream_Peek( s
, &p_data
, STREAM_PROBE_LINE
);
191 if( i_data
<= 0 ) break; /* No more data */
194 i_pos
= vlc_stream_Tell( s
);
195 if( i_pos
== 0 && i_data
>= 2 )
197 const char *psz_encoding
= NULL
;
199 if( unlikely(priv
->text
.conv
!= (vlc_iconv_t
)-1) )
200 { /* seek back to beginning? reset */
201 vlc_iconv_close( priv
->text
.conv
);
202 priv
->text
.conv
= (vlc_iconv_t
)-1;
205 if( !memcmp( p_data
, "\xFF\xFE", 2 ) )
207 psz_encoding
= "UTF-16LE";
208 priv
->text
.little_endian
= true;
210 else if( !memcmp( p_data
, "\xFE\xFF", 2 ) )
212 psz_encoding
= "UTF-16BE";
215 /* Open the converter if we need it */
216 if( psz_encoding
!= NULL
)
218 msg_Dbg( s
, "UTF-16 BOM detected" );
219 priv
->text
.conv
= vlc_iconv_open( "UTF-8", psz_encoding
);
220 if( unlikely(priv
->text
.conv
== (vlc_iconv_t
)-1) )
222 msg_Err( s
, "iconv_open failed" );
225 priv
->text
.char_width
= 2;
229 if( i_data
% priv
->text
.char_width
)
231 /* keep i_char_width boundary */
232 i_data
= i_data
- ( i_data
% priv
->text
.char_width
);
233 msg_Warn( s
, "the read is not i_char_width compatible");
239 /* Check if there is an EOL */
240 if( priv
->text
.char_width
== 1 )
243 psz_eol
= memchr( p_data
, '\n', i_data
);
244 if( psz_eol
== NULL
)
246 psz_eol
= memchr( p_data
, '\r', i_data
);
250 const uint8_t *p_last
= p_data
+ i_data
- priv
->text
.char_width
;
251 uint16_t eol
= priv
->text
.little_endian
? 0x0A00 : 0x00A0;
253 assert( priv
->text
.char_width
== 2 );
255 /* UTF-16: 000A <LF> */
256 for( const uint8_t *p
= p_data
; p
<= p_last
; p
+= 2 )
258 if( U16_AT( p
) == eol
)
260 psz_eol
= (char *)p
+ 1;
265 if( psz_eol
== NULL
)
266 { /* UTF-16: 000D <CR> */
267 eol
= priv
->text
.little_endian
? 0x0D00 : 0x00D0;
268 for( const uint8_t *p
= p_data
; p
<= p_last
; p
+= 2 )
270 if( U16_AT( p
) == eol
)
272 psz_eol
= (char *)p
+ 1;
281 i_data
= (psz_eol
- (char *)p_data
) + 1;
282 p_line
= realloc_or_free( p_line
,
283 i_line
+ i_data
+ priv
->text
.char_width
); /* add \0 */
286 i_data
= vlc_stream_Read( s
, &p_line
[i_line
], i_data
);
287 if( i_data
<= 0 ) break; /* Hmmm */
288 i_line
+= i_data
- priv
->text
.char_width
; /* skip \n */;
291 /* We have our line */
295 /* Read data (+1 for easy \0 append) */
296 p_line
= realloc_or_free( p_line
,
297 i_line
+ STREAM_PROBE_LINE
+ priv
->text
.char_width
);
300 i_data
= vlc_stream_Read( s
, &p_line
[i_line
], STREAM_PROBE_LINE
);
301 if( i_data
<= 0 ) break; /* Hmmm */
305 if( i_read
>= STREAM_LINE_MAX
)
306 goto error
; /* line too long */
311 if( priv
->text
.char_width
> 1 )
314 size_t i_in
= 0, i_out
= 0;
315 const char * p_in
= NULL
;
317 char * psz_new_line
= NULL
;
320 /* UTF-8 needs at most 150% of the buffer as many as UTF-16 */
321 i_new_line
= i_line
* 3 / 2;
322 psz_new_line
= malloc( i_new_line
);
323 if( psz_new_line
== NULL
)
325 i_in
= (size_t)i_line
;
326 i_out
= (size_t)i_new_line
;
328 p_out
= psz_new_line
;
330 if( vlc_iconv( priv
->text
.conv
, &p_in
, &i_in
, &p_out
, &i_out
) == (size_t)-1 )
332 msg_Err( s
, "conversion error: %s", vlc_strerror_c( errno
) );
333 msg_Dbg( s
, "original: %d, in %zu, out %zu", i_line
, i_in
, i_out
);
336 p_line
= psz_new_line
;
337 i_line
= (size_t)i_new_line
- i_out
; /* does not include \0 */
340 /* Remove trailing LF/CR */
341 while( i_line
>= 1 &&
342 (p_line
[i_line
- 1] == '\r' || p_line
[i_line
- 1] == '\n') )
345 /* Make sure the \0 is there */
346 p_line
[i_line
] = '\0';
352 /* We failed to read any data, probably EOF */
356 if( priv
->text
.conv
!= (vlc_iconv_t
)(-1) )
358 vlc_iconv_close( priv
->text
.conv
);
359 priv
->text
.conv
= (vlc_iconv_t
)(-1);
364 static ssize_t
vlc_stream_CopyBlock(block_t
**restrict pp
,
365 void *buf
, size_t len
)
367 block_t
*block
= *pp
;
372 if (len
> block
->i_buffer
)
373 len
= block
->i_buffer
;
376 memcpy(buf
, block
->p_buffer
, len
);
378 block
->p_buffer
+= len
;
379 block
->i_buffer
-= len
;
381 if (block
->i_buffer
== 0)
383 block_Release(block
);
387 return likely(len
> 0) ? (ssize_t
)len
: -1;
390 static ssize_t
vlc_stream_ReadRaw(stream_t
*s
, void *buf
, size_t len
)
392 stream_priv_t
*priv
= (stream_priv_t
*)s
;
395 assert(len
<= SSIZE_MAX
);
400 if (s
->pf_read
!= NULL
)
402 assert(priv
->block
== NULL
);
405 if (unlikely(len
== 0))
408 char dummy
[(len
<= 256 ? len
: 256)];
409 ret
= s
->pf_read(s
, dummy
, sizeof (dummy
));
412 ret
= s
->pf_read(s
, buf
, len
);
416 ret
= vlc_stream_CopyBlock(&priv
->block
, buf
, len
);
420 if (s
->pf_block
!= NULL
)
424 priv
->block
= s
->pf_block(s
, &eof
);
425 ret
= vlc_stream_CopyBlock(&priv
->block
, buf
, len
);
434 ssize_t
vlc_stream_ReadPartial(stream_t
*s
, void *buf
, size_t len
)
436 stream_priv_t
*priv
= (stream_priv_t
*)s
;
439 ret
= vlc_stream_CopyBlock(&priv
->peek
, buf
, len
);
443 assert(ret
<= (ssize_t
)len
);
447 ret
= vlc_stream_ReadRaw(s
, buf
, len
);
451 priv
->eof
= len
!= 0;
452 assert(ret
<= (ssize_t
)len
);
456 ssize_t
vlc_stream_Read(stream_t
*s
, void *buf
, size_t len
)
462 ssize_t ret
= vlc_stream_ReadPartial(s
, buf
, len
);
469 buf
= (char *)buf
+ ret
;
470 assert(len
>= (size_t)ret
);
478 ssize_t
vlc_stream_Peek(stream_t
*s
, const uint8_t **restrict bufp
, size_t len
)
480 stream_priv_t
*priv
= (stream_priv_t
*)s
;
493 peek
= block_Alloc(len
);
494 if (unlikely(peek
== NULL
))
500 if (peek
->i_buffer
< len
)
502 size_t avail
= peek
->i_buffer
;
504 peek
= block_TryRealloc(peek
, 0, len
);
505 if (unlikely(peek
== NULL
))
508 peek
->i_buffer
= avail
;
512 *bufp
= peek
->p_buffer
;
514 while (peek
->i_buffer
< len
)
516 size_t avail
= peek
->i_buffer
;
519 ret
= vlc_stream_ReadRaw(s
, peek
->p_buffer
+ avail
, len
- avail
);
523 peek
->i_buffer
+= ret
;
526 return peek
->i_buffer
;
532 block_t
*vlc_stream_ReadBlock(stream_t
*s
)
534 stream_priv_t
*priv
= (stream_priv_t
*)s
;
543 if (priv
->peek
!= NULL
)
548 else if (priv
->block
!= NULL
)
553 else if (s
->pf_block
!= NULL
)
556 block
= s
->pf_block(s
, &priv
->eof
);
560 block
= block_Alloc(4096);
561 if (unlikely(block
== NULL
))
564 ssize_t ret
= s
->pf_read(s
, block
->p_buffer
, block
->i_buffer
);
566 block
->i_buffer
= ret
;
569 block_Release(block
);
577 priv
->offset
+= block
->i_buffer
;
582 uint64_t vlc_stream_Tell(const stream_t
*s
)
584 const stream_priv_t
*priv
= (const stream_priv_t
*)s
;
589 bool vlc_stream_Eof(const stream_t
*s
)
591 const stream_priv_t
*priv
= (const stream_priv_t
*)s
;
596 int vlc_stream_Seek(stream_t
*s
, uint64_t offset
)
598 stream_priv_t
*priv
= (stream_priv_t
*)s
;
602 block_t
*peek
= priv
->peek
;
605 if (offset
>= priv
->offset
606 && offset
<= (priv
->offset
+ peek
->i_buffer
))
607 { /* Seeking within the peek buffer */
608 size_t fwd
= offset
- priv
->offset
;
610 peek
->p_buffer
+= fwd
;
611 peek
->i_buffer
-= fwd
;
612 priv
->offset
= offset
;
614 if (peek
->i_buffer
== 0)
625 if (priv
->offset
== offset
)
626 return VLC_SUCCESS
; /* Nothing to do! */
629 if (s
->pf_seek
== NULL
)
632 int ret
= s
->pf_seek(s
, offset
);
633 if (ret
!= VLC_SUCCESS
)
636 priv
->offset
= offset
;
644 if (priv
->block
!= NULL
)
646 block_Release(priv
->block
);
654 * Use to control the "stream_t *". Look at #stream_query_e for
655 * possible "i_query" value and format arguments. Return VLC_SUCCESS
656 * if ... succeed ;) and VLC_EGENERIC if failed or unimplemented
658 int vlc_stream_vaControl(stream_t
*s
, int cmd
, va_list args
)
660 stream_priv_t
*priv
= (stream_priv_t
*)s
;
664 case STREAM_SET_TITLE
:
665 case STREAM_SET_SEEKPOINT
:
667 int ret
= s
->pf_control(s
, cmd
, args
);
668 if (ret
!= VLC_SUCCESS
)
673 if (priv
->peek
!= NULL
)
675 block_Release(priv
->peek
);
679 if (priv
->block
!= NULL
)
681 block_Release(priv
->block
);
688 return s
->pf_control(s
, cmd
, args
);
692 * Read data into a block.
694 * @param s stream to read data from
695 * @param size number of bytes to read
696 * @return a block of data, or NULL on error
697 @ note The block size may be shorter than requested if the end-of-stream was
700 block_t
*vlc_stream_Block( stream_t
*s
, size_t size
)
702 if( unlikely(size
> SSIZE_MAX
) )
705 block_t
*block
= block_Alloc( size
);
706 if( unlikely(block
== NULL
) )
709 ssize_t val
= vlc_stream_Read( s
, block
->p_buffer
, size
);
712 block_Release( block
);
716 block
->i_buffer
= val
;
721 * Returns a node containing all the input_item of the directory pointer by
722 * this stream. returns VLC_SUCCESS on success.
724 int vlc_stream_ReadDir( stream_t
*s
, input_item_node_t
*p_node
)
726 return s
->pf_readdir( s
, p_node
);