/src/vlc/src/text/strings.c
Line | Count | Source (jump to first uncovered line) |
1 | | /***************************************************************************** |
2 | | * strings.c: String related functions |
3 | | ***************************************************************************** |
4 | | * Copyright (C) 2006 VLC authors and VideoLAN |
5 | | * Copyright (C) 2008-2009 Rémi Denis-Courmont |
6 | | * |
7 | | * Authors: Antoine Cellerier <dionoea at videolan dot org> |
8 | | * Daniel Stranger <vlc at schmaller dot de> |
9 | | * Rémi Denis-Courmont |
10 | | * |
11 | | * This program is free software; you can redistribute it and/or modify it |
12 | | * under the terms of the GNU Lesser General Public License as published by |
13 | | * the Free Software Foundation; either version 2.1 of the License, or |
14 | | * (at your option) any later version. |
15 | | * |
16 | | * This program is distributed in the hope that it will be useful, |
17 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
18 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
19 | | * GNU Lesser General Public License for more details. |
20 | | * |
21 | | * You should have received a copy of the GNU Lesser General Public License |
22 | | * along with this program; if not, write to the Free Software Foundation, |
23 | | * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. |
24 | | *****************************************************************************/ |
25 | | |
26 | | /***************************************************************************** |
27 | | * Preamble |
28 | | *****************************************************************************/ |
29 | | #ifdef HAVE_CONFIG_H |
30 | | # include "config.h" |
31 | | #endif |
32 | | |
33 | | #include <vlc_common.h> |
34 | | #include <assert.h> |
35 | | |
36 | | /* Needed by vlc_strftime */ |
37 | | #include <time.h> |
38 | | #include <limits.h> |
39 | | #include <math.h> |
40 | | #include <string.h> |
41 | | #ifndef HAVE_STRCOLL |
42 | | # define strcoll strcasecmp |
43 | | #endif |
44 | | |
45 | | /* Needed by vlc_strfplayer */ |
46 | | #include <vlc_meta.h> |
47 | | #include <vlc_aout.h> |
48 | | #include <vlc_memstream.h> |
49 | | |
50 | | #include <vlc_strings.h> |
51 | | #include <vlc_charset.h> |
52 | | #include <vlc_arrays.h> |
53 | | #include <vlc_player.h> |
54 | | #include "../libvlc.h" |
55 | | #include <errno.h> |
56 | | |
57 | | static const struct xml_entity_s |
58 | | { |
59 | | char psz_entity[8]; |
60 | | char psz_char[4]; |
61 | | } xml_entities[] = { |
62 | | /* Important: this list has to be in alphabetical order (psz_entity-wise) */ |
63 | | { "AElig;", "Æ" }, |
64 | | { "Aacute;", "Á" }, |
65 | | { "Acirc;", "Â" }, |
66 | | { "Agrave;", "À" }, |
67 | | { "Aring;", "Å" }, |
68 | | { "Atilde;", "Ã" }, |
69 | | { "Auml;", "Ä" }, |
70 | | { "Ccedil;", "Ç" }, |
71 | | { "Dagger;", "‡" }, |
72 | | { "ETH;", "Ð" }, |
73 | | { "Eacute;", "É" }, |
74 | | { "Ecirc;", "Ê" }, |
75 | | { "Egrave;", "È" }, |
76 | | { "Euml;", "Ë" }, |
77 | | { "Iacute;", "Í" }, |
78 | | { "Icirc;", "Î" }, |
79 | | { "Igrave;", "Ì" }, |
80 | | { "Iuml;", "Ï" }, |
81 | | { "Ntilde;", "Ñ" }, |
82 | | { "OElig;", "Œ" }, |
83 | | { "Oacute;", "Ó" }, |
84 | | { "Ocirc;", "Ô" }, |
85 | | { "Ograve;", "Ò" }, |
86 | | { "Oslash;", "Ø" }, |
87 | | { "Otilde;", "Õ" }, |
88 | | { "Ouml;", "Ö" }, |
89 | | { "Scaron;", "Š" }, |
90 | | { "THORN;", "Þ" }, |
91 | | { "Uacute;", "Ú" }, |
92 | | { "Ucirc;", "Û" }, |
93 | | { "Ugrave;", "Ù" }, |
94 | | { "Uuml;", "Ü" }, |
95 | | { "Yacute;", "Ý" }, |
96 | | { "Yuml;", "Ÿ" }, |
97 | | { "aacute;", "á" }, |
98 | | { "acirc;", "â" }, |
99 | | { "acute;", "´" }, |
100 | | { "aelig;", "æ" }, |
101 | | { "agrave;", "à" }, |
102 | | { "amp;", "&" }, |
103 | | { "apos;", "'" }, |
104 | | { "aring;", "å" }, |
105 | | { "atilde;", "ã" }, |
106 | | { "auml;", "ä" }, |
107 | | { "bdquo;", "„" }, |
108 | | { "brvbar;", "¦" }, |
109 | | { "ccedil;", "ç" }, |
110 | | { "cedil;", "¸" }, |
111 | | { "cent;", "¢" }, |
112 | | { "circ;", "ˆ" }, |
113 | | { "copy;", "©" }, |
114 | | { "curren;", "¤" }, |
115 | | { "dagger;", "†" }, |
116 | | { "deg;", "°" }, |
117 | | { "divide;", "÷" }, |
118 | | { "eacute;", "é" }, |
119 | | { "ecirc;", "ê" }, |
120 | | { "egrave;", "è" }, |
121 | | { "eth;", "ð" }, |
122 | | { "euml;", "ë" }, |
123 | | { "euro;", "€" }, |
124 | | { "frac12;", "½" }, |
125 | | { "frac14;", "¼" }, |
126 | | { "frac34;", "¾" }, |
127 | | { "gt;", ">" }, |
128 | | { "hellip;", "…" }, |
129 | | { "iacute;", "í" }, |
130 | | { "icirc;", "î" }, |
131 | | { "iexcl;", "¡" }, |
132 | | { "igrave;", "ì" }, |
133 | | { "iquest;", "¿" }, |
134 | | { "iuml;", "ï" }, |
135 | | { "laquo;", "«" }, |
136 | | { "ldquo;", "“" }, |
137 | | { "lsaquo;", "‹" }, |
138 | | { "lsquo;", "‘" }, |
139 | | { "lt;", "<" }, |
140 | | { "macr;", "¯" }, |
141 | | { "mdash;", "—" }, |
142 | | { "micro;", "µ" }, |
143 | | { "middot;", "·" }, |
144 | | { "nbsp;", "\xc2\xa0" }, |
145 | | { "ndash;", "–" }, |
146 | | { "not;", "¬" }, |
147 | | { "ntilde;", "ñ" }, |
148 | | { "oacute;", "ó" }, |
149 | | { "ocirc;", "ô" }, |
150 | | { "oelig;", "œ" }, |
151 | | { "ograve;", "ò" }, |
152 | | { "ordf;", "ª" }, |
153 | | { "ordm;", "º" }, |
154 | | { "oslash;", "ø" }, |
155 | | { "otilde;", "õ" }, |
156 | | { "ouml;", "ö" }, |
157 | | { "para;", "¶" }, |
158 | | { "permil;", "‰" }, |
159 | | { "plusmn;", "±" }, |
160 | | { "pound;", "£" }, |
161 | | { "quot;", "\"" }, |
162 | | { "raquo;", "»" }, |
163 | | { "rdquo;", "”" }, |
164 | | { "reg;", "®" }, |
165 | | { "rsaquo;", "›" }, |
166 | | { "rsquo;", "’" }, |
167 | | { "sbquo;", "‚" }, |
168 | | { "scaron;", "š" }, |
169 | | { "sect;", "§" }, |
170 | | { "shy;", "" }, |
171 | | { "sup1;", "¹" }, |
172 | | { "sup2;", "²" }, |
173 | | { "sup3;", "³" }, |
174 | | { "szlig;", "ß" }, |
175 | | { "thorn;", "þ" }, |
176 | | { "tilde;", "˜" }, |
177 | | { "times;", "×" }, |
178 | | { "trade;", "™" }, |
179 | | { "uacute;", "ú" }, |
180 | | { "ucirc;", "û" }, |
181 | | { "ugrave;", "ù" }, |
182 | | { "uml;", "¨" }, |
183 | | { "uuml;", "ü" }, |
184 | | { "yacute;", "ý" }, |
185 | | { "yen;", "¥" }, |
186 | | { "yuml;", "ÿ" }, |
187 | | }; |
188 | | |
189 | | static int cmp_entity (const void *key, const void *elem) |
190 | 0 | { |
191 | 0 | const struct xml_entity_s *ent = elem; |
192 | 0 | const char *name = key; |
193 | |
|
194 | 0 | return strncmp (name, ent->psz_entity, strlen (ent->psz_entity)); |
195 | 0 | } |
196 | | |
197 | | void vlc_xml_decode( char *psz_value ) |
198 | 0 | { |
199 | 0 | char *p_pos = psz_value; |
200 | |
|
201 | 0 | while ( *psz_value ) |
202 | 0 | { |
203 | 0 | if( *psz_value == '&' ) |
204 | 0 | { |
205 | 0 | if( psz_value[1] == '#' ) |
206 | 0 | { /* &#DDD; or &#xHHHH; Unicode code point */ |
207 | 0 | char *psz_end; |
208 | 0 | unsigned long cp; |
209 | |
|
210 | 0 | if( psz_value[2] == 'x' ) /* The x must be lower-case. */ |
211 | 0 | cp = strtoul( psz_value + 3, &psz_end, 16 ); |
212 | 0 | else |
213 | 0 | cp = strtoul( psz_value + 2, &psz_end, 10 ); |
214 | |
|
215 | 0 | if( *psz_end == ';' ) |
216 | 0 | { |
217 | 0 | psz_value = psz_end + 1; |
218 | 0 | if( cp == 0 ) |
219 | 0 | (void)0; /* skip nulls */ |
220 | 0 | else |
221 | 0 | if( cp <= 0x7F ) |
222 | 0 | { |
223 | 0 | *p_pos = cp; |
224 | 0 | } |
225 | 0 | else |
226 | | /* Unicode code point outside ASCII. |
227 | | * &#xxx; representation is longer than UTF-8 :) */ |
228 | 0 | if( cp <= 0x7FF ) |
229 | 0 | { |
230 | 0 | *p_pos++ = 0xC0 | (cp >> 6); |
231 | 0 | *p_pos = 0x80 | (cp & 0x3F); |
232 | 0 | } |
233 | 0 | else |
234 | 0 | if( cp <= 0xFFFF ) |
235 | 0 | { |
236 | 0 | *p_pos++ = 0xE0 | (cp >> 12); |
237 | 0 | *p_pos++ = 0x80 | ((cp >> 6) & 0x3F); |
238 | 0 | *p_pos = 0x80 | (cp & 0x3F); |
239 | 0 | } |
240 | 0 | else |
241 | 0 | if( cp <= 0x1FFFFF ) /* Outside the BMP */ |
242 | 0 | { /* Unicode stops at 10FFFF, but who cares? */ |
243 | 0 | *p_pos++ = 0xF0 | (cp >> 18); |
244 | 0 | *p_pos++ = 0x80 | ((cp >> 12) & 0x3F); |
245 | 0 | *p_pos++ = 0x80 | ((cp >> 6) & 0x3F); |
246 | 0 | *p_pos = 0x80 | (cp & 0x3F); |
247 | 0 | } |
248 | 0 | } |
249 | 0 | else |
250 | 0 | { |
251 | | /* Invalid entity number */ |
252 | 0 | *p_pos = *psz_value; |
253 | 0 | psz_value++; |
254 | 0 | } |
255 | 0 | } |
256 | 0 | else |
257 | 0 | { /* Well-known XML entity */ |
258 | 0 | const struct xml_entity_s *ent; |
259 | |
|
260 | 0 | ent = bsearch (psz_value + 1, xml_entities, |
261 | 0 | ARRAY_SIZE (xml_entities), |
262 | 0 | sizeof (*ent), cmp_entity); |
263 | 0 | if (ent != NULL) |
264 | 0 | { |
265 | 0 | size_t olen = strlen (ent->psz_char); |
266 | 0 | memcpy (p_pos, ent->psz_char, olen); |
267 | 0 | p_pos += olen - 1; |
268 | 0 | psz_value += strlen (ent->psz_entity) + 1; |
269 | 0 | } |
270 | 0 | else |
271 | 0 | { /* No match */ |
272 | 0 | *p_pos = *psz_value; |
273 | 0 | psz_value++; |
274 | 0 | } |
275 | 0 | } |
276 | 0 | } |
277 | 0 | else |
278 | 0 | { |
279 | 0 | *p_pos = *psz_value; |
280 | 0 | psz_value++; |
281 | 0 | } |
282 | |
|
283 | 0 | p_pos++; |
284 | 0 | } |
285 | |
|
286 | 0 | *p_pos = '\0'; |
287 | 0 | } |
288 | | |
289 | | char *vlc_xml_encode (const char *str) |
290 | 0 | { |
291 | 0 | struct vlc_memstream stream; |
292 | 0 | ssize_t n; |
293 | 0 | uint32_t cp; |
294 | |
|
295 | 0 | assert(str != NULL); |
296 | 0 | vlc_memstream_open(&stream); |
297 | |
|
298 | 0 | while ((n = vlc_towc (str, &cp)) != 0) |
299 | 0 | { |
300 | 0 | if (unlikely(n == -1)) |
301 | 0 | { |
302 | 0 | if (vlc_memstream_close(&stream) == 0) |
303 | 0 | free(stream.ptr); |
304 | 0 | errno = EILSEQ; |
305 | 0 | return NULL; |
306 | 0 | } |
307 | | |
308 | 0 | switch (cp) |
309 | 0 | { |
310 | 0 | case '\"': |
311 | 0 | vlc_memstream_puts(&stream, """); |
312 | 0 | break; |
313 | 0 | case '&': |
314 | 0 | vlc_memstream_puts(&stream, "&"); |
315 | 0 | break; |
316 | 0 | case '\'': |
317 | 0 | vlc_memstream_puts(&stream, "'"); |
318 | 0 | break; |
319 | 0 | case '<': |
320 | 0 | vlc_memstream_puts(&stream, "<"); |
321 | 0 | break; |
322 | 0 | case '>': |
323 | 0 | vlc_memstream_puts(&stream, ">"); |
324 | 0 | break; |
325 | 0 | default: |
326 | 0 | if (cp < 32) /* C0 code not allowed (except 9, 10 and 13) */ |
327 | 0 | break; |
328 | 0 | if (cp >= 128 && cp < 160) /* C1 code encoded (except 133) */ |
329 | 0 | { |
330 | 0 | vlc_memstream_printf(&stream, "&#%"PRIu32";", cp); |
331 | 0 | break; |
332 | 0 | } |
333 | | /* fall through */ |
334 | 0 | case 9: |
335 | 0 | case 10: |
336 | 0 | case 13: |
337 | 0 | case 133: |
338 | 0 | vlc_memstream_write(&stream, str, n); |
339 | 0 | break; |
340 | 0 | } |
341 | 0 | str += n; |
342 | 0 | } |
343 | | |
344 | 0 | if (vlc_memstream_close(&stream)) |
345 | 0 | return NULL; |
346 | 0 | return stream.ptr; |
347 | 0 | } |
348 | | |
349 | | /* Hex encoding */ |
350 | | void vlc_hex_encode_binary(const void *input, size_t size, char *output) |
351 | 0 | { |
352 | 0 | const unsigned char *buffer = input; |
353 | |
|
354 | 0 | for (size_t i = 0; i < size; i++) { |
355 | 0 | sprintf(&output[i * 2], "%02hhx", buffer[i]); |
356 | 0 | } |
357 | 0 | } |
358 | | |
359 | | /* Base64 encoding */ |
360 | | char *vlc_b64_encode_binary(const void *src, size_t length) |
361 | 0 | { |
362 | 0 | static const char b64[] = |
363 | 0 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; |
364 | 0 | const unsigned char *in = src; |
365 | 0 | char *dst = malloc((((length + 2) / 3) * 4) + 1); |
366 | 0 | char *out = dst; |
367 | |
|
368 | 0 | if (unlikely(dst == NULL)) |
369 | 0 | return NULL; |
370 | | |
371 | 0 | while (length >= 3) { /* pops (up to) 3 bytes of input, push 4 bytes */ |
372 | 0 | uint_fast32_t v = (in[0] << 16) | (in[1] << 8) | in[2]; |
373 | |
|
374 | 0 | *(out++) = b64[(v >> 18)]; |
375 | 0 | *(out++) = b64[(v >> 12) & 0x3f]; |
376 | 0 | *(out++) = b64[(v >> 6) & 0x3f]; |
377 | 0 | *(out++) = b64[(v >> 0) & 0x3f]; |
378 | 0 | in += 3; |
379 | 0 | length -= 3; |
380 | 0 | } |
381 | |
|
382 | 0 | switch (length) { |
383 | 0 | case 2: { |
384 | 0 | uint_fast16_t v = (in[0] << 8) | in[1]; |
385 | |
|
386 | 0 | *(out++) = b64[(v >> 10)]; |
387 | 0 | *(out++) = b64[(v >> 4) & 0x3f]; |
388 | 0 | *(out++) = b64[(v << 2) & 0x3f]; |
389 | 0 | *(out++) = '='; |
390 | 0 | break; |
391 | 0 | } |
392 | | |
393 | 0 | case 1: { |
394 | 0 | uint_fast8_t v = in[0]; |
395 | |
|
396 | 0 | *(out++) = b64[(v >> 2)]; |
397 | 0 | *(out++) = b64[(v << 4) & 0x3f]; |
398 | 0 | *(out++) = '='; |
399 | 0 | *(out++) = '='; |
400 | 0 | break; |
401 | 0 | } |
402 | 0 | } |
403 | | |
404 | 0 | *out = '\0'; |
405 | 0 | return dst; |
406 | 0 | } |
407 | | |
408 | | char *vlc_b64_encode(const char *src) |
409 | 0 | { |
410 | 0 | if (src == NULL) |
411 | 0 | src = ""; |
412 | 0 | return vlc_b64_encode_binary(src, strlen(src)); |
413 | 0 | } |
414 | | |
415 | | /* Base64 decoding */ |
416 | | size_t vlc_b64_decode_binary_to_buffer(void *dst, size_t size, |
417 | | const char *restrict src) |
418 | 0 | { |
419 | 0 | static const signed char b64[256] = { |
420 | 0 | -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 00-0F */ |
421 | 0 | -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 10-1F */ |
422 | 0 | -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63, /* 20-2F */ |
423 | 0 | 52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1, /* 30-3F */ |
424 | 0 | -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14, /* 40-4F */ |
425 | 0 | 15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1, /* 50-5F */ |
426 | 0 | -1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40, /* 60-6F */ |
427 | 0 | 41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1, /* 70-7F */ |
428 | 0 | -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 80-8F */ |
429 | 0 | -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* 90-9F */ |
430 | 0 | -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* A0-AF */ |
431 | 0 | -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* B0-BF */ |
432 | 0 | -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* C0-CF */ |
433 | 0 | -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* D0-DF */ |
434 | 0 | -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* E0-EF */ |
435 | 0 | -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, /* F0-FF */ |
436 | 0 | }; |
437 | 0 | const unsigned char *in = (const unsigned char *)src; |
438 | 0 | unsigned char *out = dst; |
439 | 0 | signed char prev; |
440 | 0 | int shift = 0; |
441 | |
|
442 | 0 | static_assert (CHAR_BIT == 8, "Oops"); |
443 | |
|
444 | 0 | while (size > 0) { |
445 | 0 | const signed char cur = b64[*(in++)]; |
446 | 0 | if (cur < 0) |
447 | 0 | break; |
448 | | |
449 | 0 | if (shift != 0) { |
450 | 0 | *(out++) = (prev << shift) | (cur >> (6 - shift)); |
451 | 0 | size--; |
452 | 0 | } |
453 | |
|
454 | 0 | prev = cur; |
455 | 0 | shift = (shift + 2) & 7; |
456 | 0 | } |
457 | |
|
458 | 0 | return out - (unsigned char *)dst; |
459 | 0 | } |
460 | | |
461 | | size_t vlc_b64_decode_binary( uint8_t **pp_dst, const char *psz_src ) |
462 | 0 | { |
463 | 0 | const int i_src = strlen( psz_src ); |
464 | 0 | uint8_t *p_dst; |
465 | |
|
466 | 0 | *pp_dst = p_dst = malloc( i_src ); |
467 | 0 | if( !p_dst ) |
468 | 0 | return 0; |
469 | 0 | return vlc_b64_decode_binary_to_buffer( p_dst, i_src, psz_src ); |
470 | 0 | } |
471 | | char *vlc_b64_decode( const char *psz_src ) |
472 | 0 | { |
473 | 0 | const int i_src = strlen( psz_src ); |
474 | 0 | char *p_dst = malloc( i_src + 1 ); |
475 | 0 | size_t i_dst; |
476 | 0 | if( !p_dst ) |
477 | 0 | return NULL; |
478 | | |
479 | 0 | i_dst = vlc_b64_decode_binary_to_buffer( (uint8_t*)p_dst, i_src, psz_src ); |
480 | 0 | p_dst[i_dst] = '\0'; |
481 | |
|
482 | 0 | return p_dst; |
483 | 0 | } |
484 | | |
485 | | char *vlc_strftime( const char *tformat ) |
486 | 0 | { |
487 | 0 | time_t curtime; |
488 | 0 | struct tm loctime; |
489 | |
|
490 | 0 | if (strcmp (tformat, "") == 0) |
491 | 0 | return strdup (""); /* corner case w.r.t. strftime() return value */ |
492 | | |
493 | | /* Get the current time. */ |
494 | 0 | time( &curtime ); |
495 | | |
496 | | /* Convert it to local time representation. */ |
497 | 0 | localtime_r( &curtime, &loctime ); |
498 | 0 | for (size_t buflen = strlen (tformat) + 32;; buflen += 32) |
499 | 0 | { |
500 | 0 | char *str = malloc (buflen); |
501 | 0 | if (str == NULL) |
502 | 0 | return NULL; |
503 | | |
504 | 0 | size_t len = strftime (str, buflen, tformat, &loctime); |
505 | 0 | if (len > 0) |
506 | 0 | { |
507 | 0 | char *ret = realloc (str, len + 1); |
508 | 0 | return ret ? ret : str; /* <- this cannot fail */ |
509 | 0 | } |
510 | 0 | free (str); |
511 | 0 | } |
512 | 0 | vlc_assert_unreachable (); |
513 | 0 | } |
514 | | |
515 | | static void write_duration(struct vlc_memstream *stream, vlc_tick_t duration) |
516 | 0 | { |
517 | 0 | lldiv_t d; |
518 | 0 | long long sec; |
519 | |
|
520 | 0 | duration /= CLOCK_FREQ; |
521 | 0 | d = lldiv(duration, 60); |
522 | 0 | sec = d.rem; |
523 | 0 | d = lldiv(d.quot, 60); |
524 | 0 | vlc_memstream_printf(stream, "%02lld:%02lld:%02lld", d.quot, d.rem, sec); |
525 | 0 | } |
526 | | |
527 | | static int write_meta(struct vlc_memstream *stream, input_item_t *item, |
528 | | vlc_meta_type_t type) |
529 | 0 | { |
530 | 0 | if (item == NULL) |
531 | 0 | return EOF; |
532 | | |
533 | 0 | char *value = input_item_GetMeta(item, type); |
534 | 0 | if (value == NULL) |
535 | 0 | return EOF; |
536 | | |
537 | 0 | vlc_memstream_puts(stream, value); |
538 | 0 | free(value); |
539 | 0 | return 0; |
540 | 0 | } |
541 | | |
542 | | char *vlc_strfplayer(vlc_player_t *player, input_item_t *item, const char *s) |
543 | 0 | { |
544 | 0 | struct vlc_memstream stream[1]; |
545 | |
|
546 | 0 | char c; |
547 | 0 | bool b_is_format = false; |
548 | 0 | bool b_empty_if_na = false; |
549 | |
|
550 | 0 | assert(s != NULL); |
551 | | |
552 | 0 | if (!item && player) |
553 | 0 | item = vlc_player_GetCurrentMedia(player); |
554 | |
|
555 | 0 | vlc_memstream_open(stream); |
556 | |
|
557 | 0 | while ((c = *s) != '\0') |
558 | 0 | { |
559 | 0 | s++; |
560 | |
|
561 | 0 | if (!b_is_format) |
562 | 0 | { |
563 | 0 | if (c == '$') |
564 | 0 | { |
565 | 0 | b_is_format = true; |
566 | 0 | b_empty_if_na = false; |
567 | 0 | continue; |
568 | 0 | } |
569 | | |
570 | 0 | vlc_memstream_putc(stream, c); |
571 | 0 | continue; |
572 | 0 | } |
573 | | |
574 | 0 | b_is_format = false; |
575 | |
|
576 | 0 | switch (c) |
577 | 0 | { |
578 | 0 | case 'a': |
579 | 0 | write_meta(stream, item, vlc_meta_Artist); |
580 | 0 | break; |
581 | 0 | case 'b': |
582 | 0 | write_meta(stream, item, vlc_meta_Album); |
583 | 0 | break; |
584 | 0 | case 'c': |
585 | 0 | write_meta(stream, item, vlc_meta_Copyright); |
586 | 0 | break; |
587 | 0 | case 'd': |
588 | 0 | write_meta(stream, item, vlc_meta_Description); |
589 | 0 | break; |
590 | 0 | case 'e': |
591 | 0 | write_meta(stream, item, vlc_meta_EncodedBy); |
592 | 0 | break; |
593 | 0 | case 'f': |
594 | 0 | if (item != NULL) |
595 | 0 | { |
596 | 0 | vlc_mutex_lock(&item->lock); |
597 | 0 | if (item->p_stats != NULL) |
598 | 0 | vlc_memstream_printf(stream, "%"PRIi64, |
599 | 0 | item->p_stats->i_displayed_pictures); |
600 | 0 | else if (!b_empty_if_na) |
601 | 0 | vlc_memstream_putc(stream, '-'); |
602 | 0 | vlc_mutex_unlock(&item->lock); |
603 | 0 | } |
604 | 0 | else if (!b_empty_if_na) |
605 | 0 | vlc_memstream_putc(stream, '-'); |
606 | 0 | break; |
607 | 0 | case 'g': |
608 | 0 | write_meta(stream, item, vlc_meta_Genre); |
609 | 0 | break; |
610 | 0 | case 'l': |
611 | 0 | write_meta(stream, item, vlc_meta_Language); |
612 | 0 | break; |
613 | 0 | case 'n': |
614 | 0 | write_meta(stream, item, vlc_meta_TrackNumber); |
615 | 0 | break; |
616 | 0 | case 'o': |
617 | 0 | write_meta(stream, item, vlc_meta_TrackTotal); |
618 | 0 | break; |
619 | 0 | case 'p': |
620 | 0 | if (item == NULL) |
621 | 0 | break; |
622 | 0 | { |
623 | 0 | char *value = input_item_GetNowPlayingFb(item); |
624 | 0 | if (value == NULL) |
625 | 0 | break; |
626 | | |
627 | 0 | vlc_memstream_puts(stream, value); |
628 | 0 | free(value); |
629 | 0 | } |
630 | 0 | break; |
631 | 0 | case 'r': |
632 | 0 | write_meta(stream, item, vlc_meta_Rating); |
633 | 0 | break; |
634 | 0 | case 's': |
635 | 0 | { |
636 | 0 | char *lang = NULL; |
637 | |
|
638 | 0 | if (player != NULL) |
639 | 0 | lang = vlc_player_GetCategoryLanguage(player, SPU_ES); |
640 | 0 | if (lang != NULL) |
641 | 0 | { |
642 | 0 | vlc_memstream_puts(stream, lang); |
643 | 0 | free(lang); |
644 | 0 | } |
645 | 0 | else if (!b_empty_if_na) |
646 | 0 | vlc_memstream_putc(stream, '-'); |
647 | 0 | break; |
648 | 0 | } |
649 | 0 | case 't': |
650 | 0 | write_meta(stream, item, vlc_meta_Title); |
651 | 0 | break; |
652 | 0 | case 'u': |
653 | 0 | write_meta(stream, item, vlc_meta_URL); |
654 | 0 | break; |
655 | 0 | case 'A': |
656 | 0 | write_meta(stream, item, vlc_meta_Date); |
657 | 0 | break; |
658 | 0 | case 'B': |
659 | 0 | { |
660 | 0 | if (player) |
661 | 0 | { |
662 | 0 | const struct vlc_player_track *track = |
663 | 0 | vlc_player_GetSelectedTrack(player, AUDIO_ES); |
664 | 0 | if (track) |
665 | 0 | { |
666 | 0 | vlc_memstream_printf(stream, "%u", |
667 | 0 | track->fmt.i_bitrate); |
668 | 0 | break; |
669 | 0 | } |
670 | 0 | } |
671 | 0 | if (!b_empty_if_na) |
672 | 0 | vlc_memstream_putc(stream, '-'); |
673 | 0 | break; |
674 | 0 | } |
675 | 0 | case 'C': |
676 | 0 | if (player) |
677 | 0 | { |
678 | 0 | ssize_t chapter = vlc_player_GetSelectedChapterIdx(player); |
679 | 0 | if (chapter != -1) |
680 | 0 | { |
681 | 0 | vlc_memstream_printf(stream, "%zd", chapter); |
682 | 0 | break; |
683 | 0 | } |
684 | 0 | } |
685 | 0 | if (!b_empty_if_na) |
686 | 0 | vlc_memstream_putc(stream, '-'); |
687 | 0 | break; |
688 | 0 | case 'D': |
689 | 0 | if (item != NULL) |
690 | 0 | write_duration(stream, input_item_GetDuration(item)); |
691 | 0 | else if (!b_empty_if_na) |
692 | 0 | vlc_memstream_puts(stream, "--:--:--"); |
693 | 0 | break; |
694 | 0 | case 'F': |
695 | 0 | if (item != NULL) |
696 | 0 | { |
697 | 0 | char *uri = input_item_GetURI(item); |
698 | 0 | if (uri != NULL) |
699 | 0 | { |
700 | 0 | vlc_memstream_puts(stream, uri); |
701 | 0 | free(uri); |
702 | 0 | } |
703 | 0 | } |
704 | 0 | break; |
705 | 0 | case 'I': |
706 | 0 | if (player) |
707 | 0 | { |
708 | 0 | ssize_t title = vlc_player_GetSelectedTitleIdx(player); |
709 | 0 | if (title != -1) |
710 | 0 | { |
711 | 0 | vlc_memstream_printf(stream, "%zd", title); |
712 | 0 | break; |
713 | 0 | } |
714 | 0 | } |
715 | 0 | if (!b_empty_if_na) |
716 | 0 | vlc_memstream_putc(stream, '-'); |
717 | 0 | break; |
718 | 0 | case 'L': |
719 | 0 | if (player) |
720 | 0 | { |
721 | 0 | vlc_tick_t length = vlc_player_GetLength(player); |
722 | 0 | vlc_tick_t time = vlc_player_GetTime(player); |
723 | 0 | if (length != VLC_TICK_INVALID && time != VLC_TICK_INVALID) |
724 | 0 | write_duration(stream, length - time); |
725 | 0 | } |
726 | 0 | if (!b_empty_if_na) |
727 | 0 | vlc_memstream_puts(stream, "--:--:--"); |
728 | 0 | break; |
729 | 0 | case 'N': |
730 | 0 | if (item != NULL) |
731 | 0 | { |
732 | 0 | char *name = input_item_GetName(item); |
733 | 0 | if (name != NULL) |
734 | 0 | { |
735 | 0 | vlc_memstream_puts(stream, name); |
736 | 0 | free(name); |
737 | 0 | } |
738 | 0 | } |
739 | 0 | break; |
740 | 0 | case 'O': |
741 | 0 | { |
742 | 0 | char *lang = NULL; |
743 | |
|
744 | 0 | if (player != NULL) |
745 | 0 | lang = vlc_player_GetCategoryLanguage(player, AUDIO_ES); |
746 | 0 | if (lang != NULL) |
747 | 0 | { |
748 | 0 | vlc_memstream_puts(stream, lang); |
749 | 0 | free(lang); |
750 | 0 | } |
751 | 0 | else if (!b_empty_if_na) |
752 | 0 | vlc_memstream_putc(stream, '-'); |
753 | 0 | break; |
754 | 0 | } |
755 | 0 | case 'P': |
756 | 0 | if (player) |
757 | 0 | { |
758 | 0 | float pos = vlc_player_GetPosition(player); |
759 | 0 | if (pos >= 0) |
760 | 0 | { |
761 | 0 | vlc_memstream_printf(stream, "%2.1f", pos); |
762 | 0 | break; |
763 | 0 | } |
764 | 0 | } |
765 | 0 | if (!b_empty_if_na) |
766 | 0 | vlc_memstream_puts(stream, "--.-%"); |
767 | 0 | break; |
768 | 0 | case 'R': |
769 | 0 | if (player) |
770 | 0 | vlc_memstream_printf(stream, "%.3f", |
771 | 0 | vlc_player_GetRate(player)); |
772 | 0 | else if (!b_empty_if_na) |
773 | 0 | vlc_memstream_putc(stream, '-'); |
774 | 0 | break; |
775 | 0 | case 'S': |
776 | 0 | if (player) |
777 | 0 | { |
778 | 0 | const struct vlc_player_track *track = |
779 | 0 | vlc_player_GetSelectedTrack(player, AUDIO_ES); |
780 | 0 | if (track) |
781 | 0 | { |
782 | 0 | div_t dr = div((track->fmt.audio.i_rate + 50) / 100, 10); |
783 | 0 | vlc_memstream_printf(stream, "%d.%01d", dr.quot, dr.rem); |
784 | 0 | break; |
785 | 0 | } |
786 | 0 | } |
787 | 0 | if (!b_empty_if_na) |
788 | 0 | vlc_memstream_putc(stream, '-'); |
789 | 0 | break; |
790 | 0 | case 'T': |
791 | 0 | if (player) |
792 | 0 | { |
793 | 0 | vlc_tick_t time = vlc_player_GetTime(player); |
794 | 0 | if (time != VLC_TICK_INVALID) |
795 | 0 | { |
796 | 0 | write_duration(stream, time); |
797 | 0 | break; |
798 | 0 | } |
799 | 0 | } |
800 | 0 | if (!b_empty_if_na) |
801 | 0 | vlc_memstream_puts(stream, "--:--:--"); |
802 | 0 | break; |
803 | 0 | case 'U': |
804 | 0 | write_meta(stream, item, vlc_meta_Publisher); |
805 | 0 | break; |
806 | 0 | case 'V': |
807 | 0 | { |
808 | 0 | float vol = 0.f; |
809 | |
|
810 | 0 | if (player) |
811 | 0 | { |
812 | 0 | audio_output_t *aout = vlc_player_aout_Hold(player); |
813 | 0 | if (aout != NULL) |
814 | 0 | { |
815 | 0 | vol = aout_VolumeGet(aout); |
816 | 0 | aout_Release(aout); |
817 | 0 | } |
818 | 0 | } |
819 | 0 | if (vol >= 0.f) |
820 | 0 | vlc_memstream_printf(stream, "%ld", lroundf(vol * 256.f)); |
821 | 0 | else if (!b_empty_if_na) |
822 | 0 | vlc_memstream_puts(stream, "---"); |
823 | 0 | break; |
824 | 0 | } |
825 | 0 | case '_': |
826 | 0 | vlc_memstream_putc(stream, '\n'); |
827 | 0 | break; |
828 | 0 | case 'Z': |
829 | 0 | if (item == NULL) |
830 | 0 | break; |
831 | 0 | { |
832 | 0 | char *value = input_item_GetNowPlayingFb(item); |
833 | 0 | if (value != NULL) |
834 | 0 | { |
835 | 0 | vlc_memstream_puts(stream, value); |
836 | 0 | free(value); |
837 | 0 | } |
838 | 0 | else |
839 | 0 | { |
840 | 0 | char *title = input_item_GetTitleFbName(item); |
841 | |
|
842 | 0 | if (write_meta(stream, item, vlc_meta_Artist) >= 0 |
843 | 0 | && title != NULL) |
844 | 0 | vlc_memstream_puts(stream, " - "); |
845 | |
|
846 | 0 | if (title != NULL) |
847 | 0 | { |
848 | 0 | vlc_memstream_puts(stream, title); |
849 | 0 | free(title); |
850 | 0 | } |
851 | 0 | } |
852 | 0 | } |
853 | 0 | break; |
854 | 0 | case ' ': |
855 | 0 | b_empty_if_na = true; |
856 | 0 | b_is_format = true; |
857 | 0 | break; |
858 | 0 | default: |
859 | 0 | vlc_memstream_putc(stream, c); |
860 | 0 | break; |
861 | 0 | } |
862 | 0 | } |
863 | | |
864 | 0 | if (vlc_memstream_close(stream)) |
865 | 0 | return NULL; |
866 | 0 | return stream->ptr; |
867 | 0 | } |
868 | | |
869 | | int vlc_filenamecmp(const char *a, const char *b) |
870 | 0 | { |
871 | 0 | size_t i; |
872 | 0 | char ca, cb; |
873 | | |
874 | | /* Attempt to guess if the sorting algorithm should be alphabetic |
875 | | * (i.e. collation) or numeric: |
876 | | * - If the first mismatching characters are not both digits, |
877 | | * then collation is the only option. |
878 | | * - If one of the first mismatching characters is 0 and the other is also |
879 | | * a digit, the comparands are probably left-padded numerical values. |
880 | | * It does not matter which algorithm is used: the zero will be smaller |
881 | | * than non-zero either way. |
882 | | * - Otherwise, the comparands are numerical values, and might not be |
883 | | * aligned (i.e. not same order of magnitude). If so, collation would |
884 | | * fail. So numerical comparison is performed. */ |
885 | 0 | for (i = 0; (ca = a[i]) == (cb = b[i]); i++) |
886 | 0 | if (ca == '\0') |
887 | 0 | return 0; /* strings are exactly identical */ |
888 | | |
889 | 0 | if ((unsigned)(ca - '0') > 9 || (unsigned)(cb - '0') > 9) |
890 | 0 | return strcoll(a, b); |
891 | | |
892 | 0 | unsigned long long ua = strtoull(a + i, NULL, 10); |
893 | 0 | unsigned long long ub = strtoull(b + i, NULL, 10); |
894 | | |
895 | | /* The number may be identical in two cases: |
896 | | * - leading zero (e.g. "012" and "12") |
897 | | * - overflow on both sides (#ULLONG_MAX) */ |
898 | 0 | if (ua == ub) |
899 | 0 | return strcoll(a, b); |
900 | | |
901 | 0 | return (ua > ub) ? +1 : -1; |
902 | 0 | } |
903 | | |
904 | | /** |
905 | | * Sanitize a file name. |
906 | | * |
907 | | * Remove forbidden, potentially forbidden and otherwise evil characters from |
908 | | * file names. That includes slashes, and popular characters like colon |
909 | | * (on Unix anyway). |
910 | | * |
911 | | * \warning This function should only be used for automatically generated |
912 | | * file names. Do not use this on full paths, only single file names without |
913 | | * any directory separator! |
914 | | */ |
915 | | void filename_sanitize( char *str ) |
916 | 0 | { |
917 | 0 | unsigned char c; |
918 | | |
919 | | /* Special file names, not allowed */ |
920 | 0 | if( !strcmp( str, "." ) || !strcmp( str, ".." ) ) |
921 | 0 | { |
922 | 0 | while( *str ) |
923 | 0 | *(str++) = '_'; |
924 | 0 | return; |
925 | 0 | } |
926 | | |
927 | | /* On platforms not using UTF-8, VLC cannot access non-Unicode paths. |
928 | | * Also, some file systems require Unicode file names. |
929 | | * NOTE: This may inserts '?' thus is done replacing '?' with '_'. */ |
930 | 0 | EnsureUTF8( str ); |
931 | | |
932 | | /* Avoid leading spaces to please Windows. */ |
933 | 0 | while( (c = *str) != '\0' ) |
934 | 0 | { |
935 | 0 | if( c != ' ' ) |
936 | 0 | break; |
937 | 0 | *(str++) = '_'; |
938 | 0 | } |
939 | |
|
940 | 0 | char *start = str; |
941 | |
|
942 | 0 | while( (c = *str) != '\0' ) |
943 | 0 | { |
944 | | /* Non-printable characters are not a good idea */ |
945 | 0 | if( c < 32 ) |
946 | 0 | *str = '_'; |
947 | | /* This is the list of characters not allowed by Microsoft. |
948 | | * We also black-list them on Unix as they may be confusing, and are |
949 | | * not supported by some file system types (notably CIFS). */ |
950 | 0 | else if( strchr( "/:\\*\"?|<>", c ) != NULL ) |
951 | 0 | *str = '_'; |
952 | 0 | str++; |
953 | 0 | } |
954 | | |
955 | | /* Avoid trailing spaces also to please Windows. */ |
956 | 0 | while( str > start ) |
957 | 0 | { |
958 | 0 | if( *(--str) != ' ' ) |
959 | 0 | break; |
960 | 0 | *str = '_'; |
961 | 0 | } |
962 | 0 | } |