1 /*****************************************************************************
2 * strings.c: String related functions
3 *****************************************************************************
4 * Copyright (C) 2006 the VideoLAN team
5 * Copyright (C) 2008-2009 Rémi Denis-Courmont
8 * Authors: Antoine Cellerier <dionoea at videolan dot org>
9 * Daniel Stranger <vlc at schmaller dot de>
10 * Rémi Denis-Courmont <rem # videolan org>
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
25 *****************************************************************************/
27 /*****************************************************************************
29 *****************************************************************************/
34 #include <vlc_common.h>
37 /* Needed by str_format_time */
41 /* Needed by str_format_meta */
42 #include <vlc_input.h>
44 #include <vlc_playlist.h>
45 #include <vlc_aout_intf.h>
47 #include <vlc_strings.h>
49 #include <vlc_charset.h>
55 * Decode encoded URI component. See also decode_URI().
56 * \return decoded duplicated string
58 char *decode_URI_duplicate( const char *psz
)
60 char *psz_dup
= strdup( psz
);
61 decode_URI( psz_dup
);
66 * Decode an encoded URI component in place.
67 * <b>This function does NOT decode entire URIs.</b>
68 * It decodes components (e.g. host name, directory, file name).
69 * Decoded URIs do not exist in the real world (see RFC3986 §2.4).
70 * Complete URIs are always "encoded" (or they are syntaxically invalid).
72 * Note that URI encoding is different from Javascript escaping. Especially,
73 * white spaces and Unicode non-ASCII code points are encoded differently.
75 * \return psz on success, NULL if it was not properly encoded
77 char *decode_URI( char *psz
)
79 unsigned char *in
= (unsigned char *)psz
, *out
= in
, c
;
84 while( ( c
= *in
++ ) != '\0' )
92 if( ( ( hex
[0] = *in
++ ) == 0 )
93 || ( ( hex
[1] = *in
++ ) == 0 ) )
97 *out
++ = (unsigned char)strtoul( hex
, NULL
, 0x10 );
102 /* Inserting non-ASCII or non-printable characters is unsafe,
103 * and no sane browser will send these unencoded */
104 if( ( c
< 32 ) || ( c
> 127 ) )
114 static inline bool isurisafe( int c
)
116 /* These are the _unreserved_ URI characters (RFC3986 §2.3) */
117 return ( (unsigned char)( c
- 'a' ) < 26 )
118 || ( (unsigned char)( c
- 'A' ) < 26 )
119 || ( (unsigned char)( c
- '0' ) < 10 )
120 || ( strchr( "-._~", c
) != NULL
);
123 static char *encode_URI_bytes (const char *psz_uri
, size_t len
)
125 char *psz_enc
= malloc (3 * len
+ 1), *out
= psz_enc
;
129 for (size_t i
= 0; i
< len
; i
++)
131 static const char hex
[16] = "0123456789ABCDEF";
132 uint8_t c
= *psz_uri
;
136 /* This is URI encoding, not HTTP forms:
137 * Space is encoded as '%20', not '+'. */
141 *out
++ = hex
[c
>> 4];
142 *out
++ = hex
[c
& 0xf];
148 out
= realloc (psz_enc
, out
- psz_enc
);
149 return out
? out
: psz_enc
; /* realloc() can fail (safe) */
153 * Encodes a URI component (RFC3986 §2).
155 * @param psz_uri nul-terminated UTF-8 representation of the component.
156 * Obviously, you can't pass a URI containing a nul character, but you don't
157 * want to do that, do you?
159 * @return encoded string (must be free()'d), or NULL for ENOMEM.
161 char *encode_URI_component( const char *psz_uri
)
163 return encode_URI_bytes (psz_uri
, strlen (psz_uri
));
167 static const struct xml_entity_s
172 /* Important: this list has to be in alphabetical order (psz_entity-wise) */
254 { "nbsp;", "\xc2\xa0" },
299 static int cmp_entity (const void *key
, const void *elem
)
301 const struct xml_entity_s
*ent
= elem
;
302 const char *name
= key
;
304 return strncmp (name
, ent
->psz_entity
, strlen (ent
->psz_entity
));
308 * Converts "<", ">" and "&" to "<", ">" and "&"
309 * \param string to convert
311 void resolve_xml_special_chars( char *psz_value
)
313 char *p_pos
= psz_value
;
317 if( *psz_value
== '&' )
319 if( psz_value
[1] == '#' )
320 { /* &#xxx; Unicode code point */
322 unsigned long cp
= strtoul( psz_value
+2, &psz_end
, 10 );
323 if( *psz_end
== ';' )
325 psz_value
= psz_end
+ 1;
327 (void)0; /* skip nuls */
334 /* Unicode code point outside ASCII.
335 * &#xxx; representation is longer than UTF-8 :) */
338 *p_pos
++ = 0xC0 | (cp
>> 6);
339 *p_pos
= 0x80 | (cp
& 0x3F);
344 *p_pos
++ = 0xE0 | (cp
>> 12);
345 *p_pos
++ = 0x80 | ((cp
>> 6) & 0x3F);
346 *p_pos
= 0x80 | (cp
& 0x3F);
349 if( cp
<= 0x1FFFFF ) /* Outside the BMP */
350 { /* Unicode stops at 10FFFF, but who cares? */
351 *p_pos
++ = 0xF0 | (cp
>> 18);
352 *p_pos
++ = 0x80 | ((cp
>> 12) & 0x3F);
353 *p_pos
++ = 0x80 | ((cp
>> 6) & 0x3F);
354 *p_pos
= 0x80 | (cp
& 0x3F);
359 /* Invalid entity number */
365 { /* Well-known XML entity */
366 const struct xml_entity_s
*ent
;
368 ent
= bsearch (psz_value
+ 1, xml_entities
,
369 sizeof (xml_entities
) / sizeof (*ent
),
370 sizeof (*ent
), cmp_entity
);
373 size_t olen
= strlen (ent
->psz_char
);
374 memcpy (p_pos
, ent
->psz_char
, olen
);
376 psz_value
+= strlen (ent
->psz_entity
) + 1;
398 * XML-encode an UTF-8 string
399 * \param str nul-terminated UTF-8 byte sequence to XML-encode
400 * \return XML encoded string or NULL on error
401 * (errno is set to ENOMEM or EILSEQ as appropriate)
403 char *convert_xml_special_chars (const char *str
)
405 assert (str
!= NULL
);
407 const size_t len
= strlen (str
);
408 char *const buf
= malloc (6 * len
+ 1), *ptr
= buf
;
409 if (unlikely(buf
== NULL
))
415 while ((n
= vlc_towc (str
, &cp
)) != 0)
417 if (unlikely(n
== (size_t)-1))
424 if ((cp
& ~0x0080) < 32 /* C0/C1 control codes */
425 && strchr ("\x09\x0A\x0D\x85", cp
) == NULL
)
426 ptr
+= sprintf (ptr
, "&#%"PRIu32
";", cp
);
430 case '\"': strcpy (ptr
, """); ptr
+= 6; break;
431 case '&': strcpy (ptr
, "&"); ptr
+= 5; break;
432 case '\'': strcpy (ptr
, "'"); ptr
+= 5; break;
433 case '<': strcpy (ptr
, "<"); ptr
+= 4; break;
434 case '>': strcpy (ptr
, ">"); ptr
+= 4; break;
435 default: memcpy (ptr
, str
, n
); ptr
+= n
; break;
441 ptr
= realloc (buf
, ptr
- buf
);
442 return likely(ptr
!= NULL
) ? ptr
: buf
; /* cannot fail */
445 /* Base64 encoding */
446 char *vlc_b64_encode_binary( const uint8_t *src
, size_t i_src
)
448 static const char b64
[] =
449 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
451 char *ret
= malloc( ( i_src
+ 4 ) * 4 / 3 );
459 /* pops (up to) 3 bytes of input, push 4 bytes */
464 *dst
++ = b64
[v
>> 26];
470 *dst
++ = b64
[v
>> 26];
475 v
|= *src
++ << 20; // 3/3
476 *dst
++ = ( i_src
>= 2 ) ? b64
[v
>> 26] : '='; // 3/4
480 *dst
++ = ( i_src
>= 3 ) ? b64
[v
>> 26] : '='; // 4/4
492 char *vlc_b64_encode( const char *src
)
495 return vlc_b64_encode_binary( (const uint8_t*)src
, strlen(src
) );
497 return vlc_b64_encode_binary( (const uint8_t*)"", 0 );
500 /* Base64 decoding */
501 size_t vlc_b64_decode_binary_to_buffer( uint8_t *p_dst
, size_t i_dst
, const char *p_src
)
503 static const int b64
[256] = {
504 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 00-0F */
505 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 10-1F */
506 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63, /* 20-2F */
507 52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1, /* 30-3F */
508 -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14, /* 40-4F */
509 15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1, /* 50-5F */
510 -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40, /* 60-6F */
511 41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1, /* 70-7F */
512 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 80-8F */
513 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 90-9F */
514 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* A0-AF */
515 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* B0-BF */
516 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* C0-CF */
517 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* D0-DF */
518 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* E0-EF */
519 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 /* F0-FF */
521 uint8_t *p_start
= p_dst
;
522 uint8_t *p
= (uint8_t *)p_src
;
527 for( i_level
= 0, i_last
= 0; (size_t)( p_dst
- p_start
) < i_dst
&& *p
!= '\0'; p
++ )
529 const int c
= b64
[(unsigned int)*p
];
539 *p_dst
++ = ( i_last
<< 2 ) | ( ( c
>> 4)&0x03 );
543 *p_dst
++ = ( ( i_last
<< 4 )&0xf0 ) | ( ( c
>> 2 )&0x0f );
547 *p_dst
++ = ( ( i_last
&0x03 ) << 6 ) | c
;
553 return p_dst
- p_start
;
555 size_t vlc_b64_decode_binary( uint8_t **pp_dst
, const char *psz_src
)
557 const int i_src
= strlen( psz_src
);
560 *pp_dst
= p_dst
= malloc( i_src
);
563 return vlc_b64_decode_binary_to_buffer( p_dst
, i_src
, psz_src
);
565 char *vlc_b64_decode( const char *psz_src
)
567 const int i_src
= strlen( psz_src
);
568 char *p_dst
= malloc( i_src
+ 1 );
573 i_dst
= vlc_b64_decode_binary_to_buffer( (uint8_t*)p_dst
, i_src
, psz_src
);
580 * Formats current time into a heap-allocated string.
581 * @param tformat time format (as with C strftime())
582 * @return an allocated string (must be free()'d), or NULL on memory error.
584 char *str_format_time( const char *tformat
)
589 if (strcmp (tformat
, "") == 0)
590 return strdup (""); /* corner case w.r.t. strftime() return value */
592 /* Get the current time. */
595 /* Convert it to local time representation. */
596 localtime_r( &curtime
, &loctime
);
597 for (size_t buflen
= strlen (tformat
) + 32;; buflen
+= 32)
599 char *str
= malloc (buflen
);
603 size_t len
= strftime (str
, buflen
, tformat
, &loctime
);
606 char *ret
= realloc (str
, len
+ 1);
607 return ret
? ret
: str
; /* <- this cannot fail */
613 static void format_duration (char *buf
, size_t len
, int64_t duration
)
618 duration
/= CLOCK_FREQ
;
619 d
= lldiv (duration
, 60);
621 d
= lldiv (d
.quot
, 60);
622 snprintf (buf
, len
, "%02lld:%02d:%02d", d
.quot
, (int)d
.rem
, sec
);
625 #define INSERT_STRING( string ) \
626 if( string != NULL ) \
628 int len = strlen( string ); \
629 dst = xrealloc( dst, i_size = i_size + len );\
630 memcpy( (dst+d), string, len ); \
635 /* same than INSERT_STRING, except that string won't be freed */
636 #define INSERT_STRING_NO_FREE( string ) \
638 int len = strlen( string ); \
639 dst = xrealloc( dst, i_size = i_size + len );\
640 memcpy( dst+d, string, len ); \
643 #undef str_format_meta
644 char *str_format_meta( vlc_object_t
*p_object
, const char *string
)
646 const char *s
= string
;
647 bool b_is_format
= false;
648 bool b_empty_if_na
= false;
650 int i_size
= strlen( string
) + 1; /* +1 to store '\0' */
651 char *dst
= strdup( string
);
652 if( !dst
) return NULL
;
655 input_thread_t
*p_input
= playlist_CurrentInput( pl_Get(p_object
) );
656 input_item_t
*p_item
= NULL
;
659 p_item
= input_GetItem(p_input
);
671 INSERT_STRING( input_item_GetArtist( p_item
) );
677 INSERT_STRING( input_item_GetAlbum( p_item
) );
683 INSERT_STRING( input_item_GetCopyright( p_item
) );
689 INSERT_STRING( input_item_GetDescription( p_item
) );
695 INSERT_STRING( input_item_GetEncodedBy( p_item
) );
699 if( p_item
&& p_item
->p_stats
)
701 vlc_mutex_lock( &p_item
->p_stats
->lock
);
702 snprintf( buf
, 10, "%"PRIi64
,
703 p_item
->p_stats
->i_displayed_pictures
);
704 vlc_mutex_unlock( &p_item
->p_stats
->lock
);
707 strcpy( buf
, b_empty_if_na
? "" : "-" );
708 INSERT_STRING_NO_FREE( buf
);
713 INSERT_STRING( input_item_GetGenre( p_item
) );
719 INSERT_STRING( input_item_GetLanguage( p_item
) );
725 INSERT_STRING( input_item_GetTrackNum( p_item
) );
731 INSERT_STRING( input_item_GetNowPlaying( p_item
) );
737 INSERT_STRING( input_item_GetRating( p_item
) );
742 char *psz_lang
= NULL
;
744 psz_lang
= var_GetNonEmptyString( p_input
, "sub-language" );
745 if( psz_lang
== NULL
)
746 psz_lang
= strdup( b_empty_if_na
? "" : "-" );
747 INSERT_STRING( psz_lang
);
753 INSERT_STRING( input_item_GetTitle( p_item
) );
759 INSERT_STRING( input_item_GetURL( p_item
) );
765 INSERT_STRING( input_item_GetDate( p_item
) );
771 snprintf( buf
, 10, "%"PRId64
,
772 var_GetInteger( p_input
, "bit-rate" )/1000 );
775 strcpy( buf
, b_empty_if_na
? "" : "-" );
776 INSERT_STRING_NO_FREE( buf
);
781 snprintf( buf
, 10, "%"PRId64
,
782 var_GetInteger( p_input
, "chapter" ) );
785 strcpy( buf
, b_empty_if_na
? "" : "-" );
786 INSERT_STRING_NO_FREE( buf
);
791 mtime_t i_duration
= input_item_GetDuration( p_item
);
792 format_duration (buf
, sizeof (buf
), i_duration
);
795 strcpy( buf
, b_empty_if_na
? "" : "--:--:--" );
796 INSERT_STRING_NO_FREE( buf
);
801 INSERT_STRING( input_item_GetURI( p_item
) );
807 snprintf( buf
, 10, "%"PRId64
,
808 var_GetInteger( p_input
, "title" ) );
811 strcpy( buf
, b_empty_if_na
? "" : "-" );
812 INSERT_STRING_NO_FREE( buf
);
815 if( p_item
&& p_input
)
817 mtime_t i_duration
= input_item_GetDuration( p_item
);
818 int64_t i_time
= var_GetTime( p_input
, "time" );
819 format_duration( buf
, sizeof(buf
),
820 i_duration
- i_time
);
823 strcpy( buf
, b_empty_if_na
? "" : "--:--:--" );
824 INSERT_STRING_NO_FREE( buf
);
829 INSERT_STRING( input_item_GetName( p_item
) );
836 lang
= var_GetNonEmptyString( p_input
,
839 lang
= strdup( b_empty_if_na
? "" : "-" );
840 INSERT_STRING( lang
);
846 snprintf( buf
, 10, "%2.1lf",
847 var_GetFloat( p_input
, "position" ) * 100. );
851 snprintf( buf
, 10, b_empty_if_na
? "" : "--.-%%" );
853 INSERT_STRING_NO_FREE( buf
);
858 float f
= var_GetFloat( p_input
, "rate" );
859 snprintf( buf
, 10, "%.3f", f
);
862 strcpy( buf
, b_empty_if_na
? "" : "-" );
863 INSERT_STRING_NO_FREE( buf
);
868 int r
= var_GetInteger( p_input
, "sample-rate" );
869 snprintf( buf
, 10, "%d.%d", r
/1000, (r
/100)%10 );
872 strcpy( buf
, b_empty_if_na
? "" : "-" );
873 INSERT_STRING_NO_FREE( buf
);
878 int64_t i_time
= var_GetTime( p_input
, "time" );
879 format_duration( buf
, sizeof(buf
), i_time
);
882 strcpy( buf
, b_empty_if_na
? "" : "--:--:--" );
883 INSERT_STRING_NO_FREE( buf
);
888 INSERT_STRING( input_item_GetPublisher( p_item
) );
893 audio_volume_t volume
= aout_VolumeGet( p_object
);
894 snprintf( buf
, 10, "%d", volume
);
895 INSERT_STRING_NO_FREE( buf
);
905 char *psz_now_playing
= input_item_GetNowPlaying( p_item
);
906 if ( psz_now_playing
== NULL
)
908 char *psz_temp
= input_item_GetTitleFbName( p_item
);
909 char *psz_artist
= input_item_GetArtist( p_item
);
910 if( !EMPTY_STR( psz_temp
) )
912 INSERT_STRING( psz_temp
);
913 if ( !EMPTY_STR( psz_artist
) )
914 INSERT_STRING_NO_FREE( " - " );
916 INSERT_STRING( psz_artist
);
919 INSERT_STRING( psz_now_playing
);
924 b_empty_if_na
= true;
938 b_empty_if_na
= false;
950 vlc_object_release( p_input
);
955 #undef INSERT_STRING_NO_FREE
959 * Apply str format time and str format meta
961 char *str_format( vlc_object_t
*p_this
, const char *psz_src
)
963 char *psz_buf1
, *psz_buf2
;
964 psz_buf1
= str_format_time( psz_src
);
965 psz_buf2
= str_format_meta( p_this
, psz_buf1
);
971 * Remove forbidden characters from filenames (including slashes)
973 void filename_sanitize( char *str
)
975 #if defined( WIN32 ) || defined( __OS2__ )
976 char *str_base
= str
;
979 if( *str
== '.' && (str
[1] == '\0' || (str
[1] == '.' && str
[2] == '\0' ) ) )
989 #if defined( WIN32 ) || defined( __OS2__ )
990 // Change leading spaces into underscores
991 while( *str
&& *str
== ' ' )
1000 #if defined( __APPLE__ )
1002 #elif defined( WIN32 ) || defined( __OS2__ )
1017 #if defined( WIN32 ) || defined( __OS2__ )
1018 // Change trailing spaces into underscores
1020 while( str
!= str_base
)
1030 * Remove forbidden characters from full paths (leaves slashes)
1032 void path_sanitize( char *str
)
1034 #if defined( WIN32 ) || defined( __OS2__ )
1035 /* check drive prefix if path is absolute */
1036 if( (((unsigned char)(str
[0] - 'A') < 26)
1037 || ((unsigned char)(str
[0] - 'a') < 26)) && (':' == str
[1]) )
1042 #if defined( __APPLE__ )
1045 #elif defined( WIN32 ) || defined( __OS2__ )
1046 if( strchr( "*\"?:|<>", *str
) )
1049 *str
= DIR_SEP_CHAR
;
1055 #include <vlc_url.h>
1061 * Convert a file path to a URI.
1062 * If already a URI, return a copy of the string.
1063 * @param path path to convert (or URI to copy)
1064 * @param scheme URI scheme to use (default is auto: "file", "fd" or "smb")
1065 * @return a nul-terminated URI string (use free() to release it),
1066 * or NULL in case of error
1068 char *make_URI (const char *path
, const char *scheme
)
1072 if (scheme
== NULL
&& !strcmp (path
, "-"))
1073 return strdup ("fd://0"); // standard input
1074 if (strstr (path
, "://") != NULL
)
1075 return strdup (path
); /* Already a URI */
1076 /* Note: VLC cannot handle URI schemes without double slash after the
1077 * scheme name (such as mailto: or news:). */
1080 #if defined( WIN32 ) || defined( __OS2__ )
1082 if (isalpha ((unsigned char)path
[0]) && (path
[1] == ':'))
1084 if (asprintf (&buf
, "%s:///%c:", scheme
? scheme
: "file",
1088 # warning Drive letter-relative path not implemented!
1089 if (path
[0] != DIR_SEP_CHAR
)
1094 if (!strncmp (path
, "\\\\", 2))
1095 { /* Windows UNC paths */
1096 #if !defined( WIN32 ) && !defined( __OS2__ )
1098 return NULL
; /* remote files not supported */
1100 /* \\host\share\path -> smb://host/share/path */
1101 if (strchr (path
+ 2, '\\') != NULL
)
1102 { /* Convert backslashes to slashes */
1103 char *dup
= strdup (path
);
1106 for (size_t i
= 2; dup
[i
]; i
++)
1108 dup
[i
] = DIR_SEP_CHAR
;
1110 char *ret
= make_URI (dup
, scheme
);
1114 # define SMB_SCHEME "smb"
1116 /* \\host\share\path -> file://host/share/path */
1117 # define SMB_SCHEME "file"
1119 size_t hostlen
= strcspn (path
+ 2, DIR_SEP
);
1121 buf
= malloc (sizeof (SMB_SCHEME
) + 3 + hostlen
);
1123 snprintf (buf
, sizeof (SMB_SCHEME
) + 3 + hostlen
,
1124 SMB_SCHEME
"://%s", path
+ 2);
1125 path
+= 2 + hostlen
;
1127 if (path
[0] == '\0')
1128 return buf
; /* Hostname without path */
1131 if (path
[0] != DIR_SEP_CHAR
)
1132 { /* Relative path: prepend the current working directory */
1135 if ((cwd
= vlc_getcwd ()) == NULL
)
1137 if (asprintf (&buf
, "%s"DIR_SEP
"%s", cwd
, path
) == -1)
1141 ret
= (buf
!= NULL
) ? make_URI (buf
, scheme
) : NULL
;
1146 if (asprintf (&buf
, "%s://", scheme
? scheme
: "file") == -1)
1151 assert (path
[0] == DIR_SEP_CHAR
);
1153 /* Absolute file path */
1154 for (const char *ptr
= path
+ 1;; ptr
++)
1156 size_t len
= strcspn (ptr
, DIR_SEP
);
1157 char *component
= encode_URI_bytes (ptr
, len
);
1158 if (component
== NULL
)
1164 int val
= asprintf (&uri
, "%s/%s", buf
, component
);
1177 * Tries to convert a URI to a local (UTF-8-encoded) file path.
1178 * @param url URI to convert
1179 * @return NULL on error, a nul-terminated string otherwise
1180 * (use free() to release it)
1182 char *make_path (const char *url
)
1187 char *path
= strstr (url
, "://");
1189 return NULL
; /* unsupported scheme or invalid syntax */
1191 end
= memchr (url
, '/', path
- url
);
1192 size_t schemelen
= ((end
!= NULL
) ? end
: path
) - url
;
1193 path
+= 3; /* skip "://" */
1195 /* Remove HTML anchor if present */
1196 end
= strchr (path
, '#');
1198 path
= strndup (path
, end
- path
);
1200 path
= strdup (path
);
1201 if (unlikely(path
== NULL
))
1202 return NULL
; /* boom! */
1207 if (schemelen
== 4 && !strncasecmp (url
, "file", 4))
1209 #if (!defined (WIN32) && !defined (__OS2__)) || defined (UNDER_CE)
1210 /* Leading slash => local path */
1213 /* Local path disguised as a remote one */
1214 if (!strncasecmp (path
, "localhost/", 10))
1215 return memmove (path
, path
+ 9, strlen (path
+ 9) + 1);
1217 for (char *p
= strchr (path
, '/'); p
; p
= strchr (p
+ 1, '/'))
1220 /* Leading backslash => local path */
1222 return memmove (path
, path
+ 1, strlen (path
+ 1) + 1);
1223 /* Local path disguised as a remote one */
1224 if (!strncasecmp (path
, "localhost\\", 10))
1225 return memmove (path
, path
+ 10, strlen (path
+ 10) + 1);
1227 if (*path
&& asprintf (&ret
, "\\\\%s", path
) == -1)
1230 /* non-local path :-( */
1233 if (schemelen
== 2 && !strncasecmp (url
, "fd", 2))
1235 int fd
= strtol (path
, &end
, 0);
1240 #if !defined( WIN32 ) && !defined( __OS2__ )
1244 ret
= strdup ("/dev/stdin");
1247 ret
= strdup ("/dev/stdout");
1250 ret
= strdup ("/dev/stderr");
1253 if (asprintf (&ret
, "/dev/fd/%d", fd
) == -1)
1257 /* XXX: Does this work on WinCE? */
1259 ret
= strdup ("CON");
1267 return ret
; /* unknown scheme */