Line data Source code
1 : /*
2 : * GPAC - Multimedia Framework C SDK
3 : *
4 : * Authors: Jean Le Feuvre - Copyright (c) Telecom ParisTech 2000-2020
5 : * Romain Bouqueau - Copyright (c) Romain Bouqueau 2015
6 : * All rights reserved
7 : *
8 : * This file is part of GPAC / common tools sub-project
9 : *
10 : * GPAC is free software; you can redistribute it and/or modify
11 : * it under the terms of the GNU Lesser General Public License as published by
12 : * the Free Software Foundation; either version 2, or (at your option)
13 : * any later version.
14 : *
15 : * GPAC 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.
19 : *
20 : * You should have received a copy of the GNU Lesser General Public
21 : * License along with this library; see the file COPYING. If not, write to
22 : * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
23 : *
24 : */
25 :
26 : #include <gpac/tools.h>
27 : #include <gpac/utf.h>
28 :
29 : #if defined(_WIN32_WCE)
30 :
31 : #include <winbase.h>
32 : #include <tlhelp32.h>
33 :
34 : #elif defined(WIN32)
35 :
36 : #include <windows.h>
37 : #include <process.h>
38 : #include <direct.h>
39 : #include <sys/stat.h>
40 : #include <share.h>
41 :
42 : #else
43 :
44 : #include <sys/stat.h>
45 : #include <unistd.h>
46 : #include <dirent.h>
47 : #include <sys/time.h>
48 :
49 : #ifndef __BEOS__
50 : #include <errno.h>
51 : #endif
52 :
53 : #endif
54 :
55 :
56 : GF_EXPORT
57 3 : GF_Err gf_rmdir(const char *DirPathName)
58 : {
59 : #if defined (_WIN32_WCE)
60 : TCHAR swzName[MAX_PATH];
61 : BOOL res;
62 : CE_CharToWide(DirPathName, swzName);
63 : res = RemoveDirectory(swzName);
64 : if (! res) {
65 : int err = GetLastError();
66 : GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("Cannot delete directory \"%s\": last error %d\n", DirPathName, err ));
67 : }
68 : #elif defined (WIN32)
69 : int res;
70 : wchar_t* wcsDirPathName = gf_utf8_to_wcs(DirPathName);
71 : if (!wcsDirPathName)
72 : return GF_IO_ERR;
73 : res = _wrmdir(wcsDirPathName);
74 : gf_free(wcsDirPathName);
75 : if (res == -1) {
76 : int err = GetLastError();
77 : GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("Cannot delete directory \"%s\": last error %d\n", DirPathName, err ));
78 : return GF_IO_ERR;
79 : }
80 : #else
81 3 : int res = rmdir(DirPathName);
82 3 : if (res==-1) {
83 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("Cannot delete directory \"%s\": last error %d\n", DirPathName, errno ));
84 : return GF_IO_ERR;
85 : }
86 : #endif
87 : return GF_OK;
88 : }
89 :
90 : GF_EXPORT
91 63 : GF_Err gf_mkdir(const char* DirPathName)
92 : {
93 : #if defined (_WIN32_WCE)
94 : TCHAR swzName[MAX_PATH];
95 : BOOL res;
96 : CE_CharToWide(DirPathName, swzName);
97 : res = CreateDirectory(swzName, NULL);
98 : if (! res) {
99 : int err = GetLastError();
100 : GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("Cannot create directory \"%s\": last error %d\n", DirPathName, err ));
101 : }
102 : #elif defined (WIN32)
103 : int res;
104 : wchar_t* wcsDirPathName = gf_utf8_to_wcs(DirPathName);
105 : if (!wcsDirPathName)
106 : return GF_IO_ERR;
107 : res = _wmkdir(wcsDirPathName);
108 : gf_free(wcsDirPathName);
109 : if (res==-1) {
110 : int err = GetLastError();
111 : if (err != 183) {
112 : // don't throw error if dir already exists
113 : GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("Cannot create directory \"%s\": last error %d\n", DirPathName, err));
114 : }
115 : }
116 : #else
117 63 : int res = mkdir(DirPathName, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
118 63 : if (res==-1) {
119 0 : if(errno == 17) {
120 0 : GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("Cannot create directory \"%s\", it already exists: last error %d \n", DirPathName, errno ));
121 : return GF_OK;
122 : } else {
123 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("Cannot create directory \"%s\": last error %d\n", DirPathName, errno ));
124 : return GF_IO_ERR;
125 : }
126 : }
127 : #endif
128 : return GF_OK;
129 : }
130 :
131 :
132 : GF_EXPORT
133 147408 : Bool gf_dir_exists(const char* DirPathName)
134 : {
135 : #if defined (_WIN32_WCE)
136 : TCHAR swzName[MAX_PATH];
137 : DWORD att;
138 : CE_CharToWide(DirPathName, swzName);
139 : att = GetFileAttributes(swzName);
140 : return (att != INVALID_FILE_ATTRIBUTES && (att & FILE_ATTRIBUTE_DIRECTORY)) ? GF_TRUE : GF_FALSE;
141 : #elif defined (WIN32)
142 : DWORD att;
143 : wchar_t* wcsDirPathName = gf_utf8_to_wcs(DirPathName);
144 : if (!wcsDirPathName)
145 : return GF_FALSE;
146 : att = GetFileAttributesW(wcsDirPathName);
147 : gf_free(wcsDirPathName);
148 : return (att != INVALID_FILE_ATTRIBUTES && (att & FILE_ATTRIBUTE_DIRECTORY)) ? GF_TRUE : GF_FALSE;
149 : #else
150 147408 : DIR* dir = opendir(DirPathName);
151 147408 : if (!dir) return GF_FALSE;
152 79380 : closedir(dir);
153 79380 : return GF_TRUE;
154 : #endif
155 : return GF_FALSE;
156 : }
157 2 : static Bool delete_dir(void *cbck, char *item_name, char *item_path, GF_FileEnumInfo *file_info)
158 : {
159 2 : Bool directory_clean_mode = *(Bool*)cbck;
160 :
161 2 : if(directory_clean_mode) {
162 1 : gf_dir_cleanup(item_path);
163 1 : gf_rmdir(item_path);
164 : } else {
165 1 : gf_file_delete(item_path);
166 : }
167 2 : return GF_FALSE;
168 : }
169 :
170 : GF_EXPORT
171 3 : GF_Err gf_dir_cleanup(const char* DirPathName)
172 : {
173 : Bool directory_clean_mode;
174 :
175 3 : directory_clean_mode = GF_TRUE;
176 3 : gf_enum_directory(DirPathName, GF_TRUE, delete_dir, &directory_clean_mode, NULL);
177 3 : directory_clean_mode = GF_FALSE;
178 3 : gf_enum_directory(DirPathName, GF_FALSE, delete_dir, &directory_clean_mode, NULL);
179 :
180 3 : return GF_OK;
181 : }
182 :
183 : GF_EXPORT
184 305 : GF_Err gf_file_delete(const char *fileName)
185 : {
186 305 : if (!fileName || !fileName[0]) {
187 1 : GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("gf_file_delete with no param - ignoring\n"));
188 : return GF_OK;
189 : }
190 : #if defined(_WIN32_WCE)
191 : TCHAR swzName[MAX_PATH];
192 : CE_CharToWide((char*)fileName, swzName);
193 : return (DeleteFile(swzName)==0) ? GF_IO_ERR : GF_OK;
194 : #elif defined(WIN32)
195 : /* success if != 0 */
196 : {
197 : BOOL op_result;
198 : wchar_t* wcsFileName = gf_utf8_to_wcs(fileName);
199 : if (!wcsFileName)
200 : return GF_IO_ERR;
201 : op_result = DeleteFileW(wcsFileName);
202 : gf_free(wcsFileName);
203 : return (op_result==0) ? GF_IO_ERR : GF_OK;
204 : }
205 : #else
206 : /* success is == 0 */
207 304 : return ( remove(fileName) == 0) ? GF_OK : GF_IO_ERR;
208 : #endif
209 : }
210 :
211 : #ifndef WIN32
212 : /**
213 : * Remove existing single-quote from a single-quoted string.
214 : * The caller is responsible for deallocating the returns string with gf_free()
215 : */
216 158 : static char* gf_sanetize_single_quoted_string(const char *src) {
217 : int i, j;
218 158 : char *out = (char*)gf_malloc(4*strlen(src)+3);
219 158 : out[0] = '\'';
220 7138 : for (i=0, j=1; (out[j]=src[i]); ++i, ++j) {
221 6980 : if (src[i]=='\'') {
222 0 : out[++j]='\\';
223 0 : out[++j]='\'';
224 0 : out[++j]='\'';
225 : }
226 : }
227 158 : out[j++] = '\'';
228 158 : out[j++] = 0;
229 158 : return out;
230 : }
231 : #endif
232 :
233 : #if defined(GPAC_CONFIG_IOS) && (__IPHONE_OS_VERSION_MAX_ALLOWED >= 80000)
234 : #include <spawn.h>
235 : extern char **environ;
236 : #endif
237 :
238 : #if defined(WIN32)
239 : #include <io.h>
240 : #endif
241 :
242 : GF_EXPORT
243 78914 : Bool gf_file_exists_ex(const char *fileName, const char *par_name)
244 : {
245 : u32 gfio_type = 0;
246 78914 : if (!fileName) return GF_FALSE;
247 :
248 78914 : if (!strncmp(fileName, "gfio://", 7))
249 : gfio_type = 1;
250 78914 : else if (par_name && !strncmp(par_name, "gfio://", 7))
251 : gfio_type = 2;
252 :
253 : if (gfio_type) {
254 : GF_FileIO *gfio_ref;
255 : GF_FileIO *new_gfio;
256 : GF_Err e;
257 : Bool res = GF_TRUE;
258 0 : if (gfio_type==1)
259 0 : gfio_ref = gf_fileio_from_url(fileName);
260 : else
261 0 : gfio_ref = gf_fileio_from_url(par_name);
262 :
263 0 : if (!gfio_ref) return GF_FALSE;
264 0 : new_gfio = gf_fileio_open_url(gfio_ref, fileName, "probe", &e);
265 0 : if (e==GF_URL_ERROR)
266 : res = GF_FALSE;
267 0 : if (new_gfio)
268 0 : gf_fileio_open_url(new_gfio, NULL, "r", &e);
269 : return res;
270 : }
271 :
272 : #if defined(WIN32)
273 : Bool res;
274 : wchar_t* wname = gf_utf8_to_wcs(fileName);
275 : if (!wname) return GF_FALSE;
276 : res = (_waccess(wname, 4) == -1) ? GF_FALSE : GF_TRUE;
277 : if (res == GF_TRUE) {
278 : DWORD att;
279 : att = GetFileAttributesW(wname);
280 : if (att != INVALID_FILE_ATTRIBUTES && (att & FILE_ATTRIBUTE_DIRECTORY))
281 : res = GF_FALSE;
282 : }
283 : gf_free(wname);
284 : return res;
285 : #elif defined(GPAC_CONFIG_LINUX)
286 78914 : Bool res = (access(fileName, 4) == -1) ? GF_FALSE : GF_TRUE;
287 78914 : if (res && gf_dir_exists(fileName))
288 : res = GF_FALSE;
289 : return res;
290 : #elif defined(__DARWIN__) || defined(__APPLE__)
291 : Bool res = (access(fileName, 4) == -1) ? GF_FALSE : GF_TRUE;
292 : if (res && gf_dir_exists(fileName))
293 : res = GF_FALSE;
294 : return res;
295 : #elif defined(GPAC_CONFIG_IOS) || defined(GPAC_CONFIG_ANDROID)
296 : Bool res = (access(fileName, 4) == -1) ? GF_FALSE : GF_TRUE;
297 : if (res && gf_dir_exists(fileName))
298 : res = GF_FALSE;
299 : return res;
300 : #else
301 : FILE *f = gf_fopen(fileName, "r");
302 : if (f) {
303 : gf_fclose(f);
304 : return GF_TRUE;
305 : }
306 : return GF_FALSE;
307 : #endif
308 : }
309 :
310 : GF_EXPORT
311 78858 : Bool gf_file_exists(const char *fileName)
312 : {
313 78858 : return gf_file_exists_ex(fileName, NULL);
314 : }
315 :
316 : GF_EXPORT
317 79 : GF_Err gf_file_move(const char *fileName, const char *newFileName)
318 : {
319 : #if defined(_WIN32_WCE)
320 : GF_Err e = GF_OK;
321 : TCHAR swzName[MAX_PATH];
322 : TCHAR swzNewName[MAX_PATH];
323 : CE_CharToWide((char*)fileName, swzName);
324 : CE_CharToWide((char*)newFileName, swzNewName);
325 : if (MoveFile(swzName, swzNewName) == 0 )
326 : e = GF_IO_ERR;
327 : #elif defined(WIN32)
328 : GF_Err e = GF_OK;
329 : /* success if != 0 */
330 : BOOL op_result;
331 : wchar_t* wcsFileName = gf_utf8_to_wcs(fileName);
332 : wchar_t* wcsNewFileName = gf_utf8_to_wcs(newFileName);
333 : if (!wcsFileName || !wcsNewFileName) {
334 : if (wcsFileName) gf_free(wcsFileName);
335 : if (wcsNewFileName) gf_free(wcsNewFileName);
336 : e = GF_IO_ERR;
337 : } else {
338 : op_result = MoveFileW(wcsFileName, wcsNewFileName);
339 : gf_free(wcsFileName);
340 : gf_free(wcsNewFileName);
341 : if ( op_result == 0 )
342 : e = GF_IO_ERR;
343 : }
344 : #else
345 : GF_Err e = GF_IO_ERR;
346 : char cmd[1024], *arg1, *arg2;
347 79 : if (!fileName || !newFileName) {
348 : e = GF_IO_ERR;
349 : } else {
350 79 : arg1 = gf_sanetize_single_quoted_string(fileName);
351 79 : arg2 = gf_sanetize_single_quoted_string(newFileName);
352 79 : if (snprintf(cmd, sizeof cmd, "mv %s %s", arg1, arg2) >= sizeof cmd) goto error;
353 :
354 : #if defined(GPAC_CONFIG_IOS) && (__IPHONE_OS_VERSION_MAX_ALLOWED >= 80000)
355 : {
356 : pid_t pid;
357 : char *argv[3];
358 : argv[0] = "mv";
359 : argv[1] = cmd;
360 : argv[2] = NULL;
361 : posix_spawn(&pid, argv[0], NULL, NULL, argv, environ);
362 : waitpid(pid, NULL, 0);
363 : }
364 : #else
365 79 : e = (system(cmd) == 0) ? GF_OK : GF_IO_ERR;
366 : #endif
367 :
368 79 : error:
369 79 : gf_free(arg1);
370 79 : gf_free(arg2);
371 : }
372 : #endif
373 :
374 79 : if (e) {
375 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[core] Failed to move file %s to %s: %s\n", fileName, newFileName, gf_error_to_string(e) ));
376 : }
377 79 : return e;
378 : }
379 :
380 : GF_EXPORT
381 357 : u64 gf_file_modification_time(const char *filename)
382 : {
383 : #if defined(_WIN32_WCE)
384 : WCHAR _file[GF_MAX_PATH];
385 : WIN32_FIND_DATA FindData;
386 : HANDLE fh;
387 : ULARGE_INTEGER uli;
388 : ULONGLONG time_ms;
389 : BOOL ret;
390 : CE_CharToWide((char *) filename, _file);
391 : fh = FindFirstFile(_file, &FindData);
392 : if (fh == INVALID_HANDLE_VALUE) return 0;
393 : uli.LowPart = FindData.ftLastWriteTime.dwLowDateTime;
394 : uli.HighPart = FindData.ftLastWriteTime.dwHighDateTime;
395 : ret = FindClose(fh);
396 : if (!ret) {
397 : DWORD err = GetLastError();
398 : GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[core] FindClose() in gf_file_modification_time() returned the following error code: %d\n", err));
399 : }
400 : time_ms = uli.QuadPart/10000;
401 : return time_ms;
402 : #elif defined(WIN32) && !defined(__GNUC__)
403 : struct _stat64 sb;
404 : int op_result;
405 : wchar_t* wcsFilename = gf_utf8_to_wcs(filename);
406 : if (!wcsFilename)
407 : return 0;
408 : op_result = _wstat64(wcsFilename, &sb);
409 : gf_free(wcsFilename);
410 : if (op_result != 0) return 0;
411 : return sb.st_mtime;
412 : #else
413 : struct stat sb;
414 357 : if (stat(filename, &sb) != 0) return 0;
415 357 : return sb.st_mtime;
416 : #endif
417 : return 0;
418 : }
419 :
420 : #include <gpac/list.h>
421 : extern int gf_mem_track_enabled;
422 : static GF_List * gpac_open_files = NULL;
423 :
424 : typedef struct
425 : {
426 : FILE *ptr;
427 : char *url;
428 : Bool is_temp;
429 : } GF_FileHandle;
430 :
431 : static u32 gpac_file_handles = 0;
432 :
433 :
434 :
435 : GF_EXPORT
436 6147 : u32 gf_file_handles_count()
437 : {
438 : #ifdef GPAC_MEMORY_TRACKING
439 6147 : if (gpac_open_files) {
440 1 : return gf_list_count(gpac_open_files);
441 : }
442 : #endif
443 6146 : return gpac_file_handles;
444 : }
445 :
446 : #ifdef GPAC_MEMORY_TRACKING
447 2 : const char *enum_open_handles(u32 *idx)
448 : {
449 : GF_FileHandle *h;
450 2 : u32 count = gf_list_count(gpac_open_files);
451 2 : if (*idx >= count) return NULL;
452 1 : h = gf_list_get(gpac_open_files, *idx);
453 1 : (*idx)++;
454 1 : return h->url;
455 : }
456 : #endif
457 :
458 58620 : static void gf_register_file_handle(char *filename, FILE *ptr, Bool is_temp_file)
459 : {
460 58620 : if (is_temp_file
461 : #ifdef GPAC_MEMORY_TRACKING
462 58620 : || gf_mem_track_enabled
463 : #endif
464 : ) {
465 : GF_FileHandle *h;
466 58469 : if (!gpac_open_files) gpac_open_files = gf_list_new();
467 58469 : GF_SAFEALLOC(h, GF_FileHandle);
468 58469 : if (h) {
469 58469 : h->ptr = ptr;
470 58469 : if (is_temp_file) {
471 0 : h->is_temp = GF_TRUE;
472 0 : h->url = filename;
473 : } else {
474 58469 : h->url = gf_strdup(filename);
475 : }
476 58469 : gf_list_add(gpac_open_files, h);
477 : }
478 : }
479 58620 : gpac_file_handles++;
480 58620 : }
481 :
482 : #include <gpac/thread.h>
483 : extern GF_Mutex *logs_mx;
484 :
485 58615 : static Bool gf_unregister_file_handle(FILE *ptr)
486 : {
487 : u32 i, count;
488 : Bool res = GF_FALSE;
489 : assert(gpac_file_handles);
490 58615 : gpac_file_handles--;
491 :
492 58615 : if (!gpac_open_files)
493 : return GF_FALSE;
494 :
495 58464 : gf_mx_p(logs_mx);
496 58464 : count = gf_list_count(gpac_open_files);
497 155400 : for (i=0; i<count; i++) {
498 155400 : GF_FileHandle *h = gf_list_get(gpac_open_files, i);
499 155400 : if (h->ptr != ptr) continue;
500 :
501 58464 : if (h->is_temp) {
502 : GF_Err e;
503 0 : fclose(h->ptr);
504 0 : e = gf_file_delete(h->url);
505 0 : if (e) {
506 0 : GF_LOG(GF_LOG_WARNING, GF_LOG_CORE, ("[Core] Failed to delete temp file %s: %s\n", h->url, gf_error_to_string(e)));
507 : }
508 : res = GF_TRUE;
509 : }
510 58464 : gf_free(h->url);
511 58464 : gf_free(h);
512 58464 : gf_list_rem(gpac_open_files, i);
513 58464 : if (!gf_list_count(gpac_open_files)) {
514 29800 : gf_list_del(gpac_open_files);
515 29800 : gpac_open_files = NULL;
516 : }
517 : break;
518 : }
519 58464 : gf_mx_v(logs_mx);
520 58464 : return res;
521 : }
522 :
523 2778 : static FILE *gf_file_temp_os(char ** const fileName)
524 : {
525 : FILE *res;
526 : #if defined(_WIN32_WCE)
527 : TCHAR pPath[MAX_PATH+1];
528 : TCHAR pTemp[MAX_PATH+1];
529 : res = NULL;
530 : if (!GetTempPath(MAX_PATH, pPath)) {
531 : pPath[0] = '.';
532 : pPath[1] = '.';
533 : }
534 : if (GetTempFileName(pPath, TEXT("git"), 0, pTemp))
535 : res = _wfopen(pTemp, TEXT("w+b"));
536 : #elif defined(WIN32)
537 : res = tmpfile();
538 :
539 : if (!res) {
540 : wchar_t tmp[MAX_PATH];
541 :
542 : GF_LOG(GF_LOG_INFO, GF_LOG_CORE, ("[Win32] system failure for tmpfile(): 0x%08x\n", GetLastError()));
543 :
544 : /*tmpfile() may fail under vista ...*/
545 : if (GetEnvironmentVariableW(L"TEMP", tmp, MAX_PATH)) {
546 : wchar_t tmp2[MAX_PATH], *t_file;
547 : char* mbs_t_file;
548 : gf_rand_init(GF_FALSE);
549 : swprintf(tmp2, MAX_PATH, L"gpac_%d_%08x_", _getpid(), gf_rand());
550 : t_file = _wtempnam(tmp, tmp2);
551 : mbs_t_file = gf_wcs_to_utf8(t_file);
552 : if (!mbs_t_file)
553 : return 0;
554 : res = gf_fopen(mbs_t_file, "w+b");
555 : if (res) {
556 : gf_unregister_file_handle(res);
557 : if (fileName) {
558 : *fileName = gf_strdup(mbs_t_file);
559 : } else {
560 : GF_LOG(GF_LOG_WARNING, GF_LOG_CORE, ("[Win32] temporary file \"%s\" won't be deleted - contact the GPAC team\n", mbs_t_file));
561 : }
562 : }
563 : gf_free(mbs_t_file);
564 : free(t_file);
565 : }
566 : }
567 : #else
568 2778 : res = tmpfile();
569 : #endif
570 :
571 2778 : if (res) {
572 2778 : gf_register_file_handle("temp file", res, GF_FALSE);
573 : }
574 2778 : return res;
575 : }
576 :
577 : GF_EXPORT
578 2778 : FILE *gf_file_temp(char ** const fileName)
579 : {
580 2778 : const char *tmp_dir = gf_opts_get_key("core", "tmp");
581 :
582 2778 : if (tmp_dir && tmp_dir[0]) {
583 : FILE *res;
584 0 : char *opath=NULL, szFName[100];
585 0 : u32 len = (u32) strlen(tmp_dir);
586 :
587 0 : gf_dynstrcat(&opath, tmp_dir, NULL);
588 0 : if (!strchr("/\\", tmp_dir[len-1]))
589 0 : gf_dynstrcat(&opath, "/", NULL);
590 0 : if (!opath) return NULL;
591 :
592 0 : sprintf(szFName, "_libgpac_%d_%p_"LLU"_%d", gf_sys_get_process_id(), opath, gf_sys_clock_high_res(), gf_rand() );
593 0 : gf_dynstrcat(&opath, szFName, NULL);
594 0 : if (fileName) {
595 : sprintf(szFName, "%p", fileName);
596 0 : gf_dynstrcat(&opath, szFName, "_");
597 : }
598 :
599 0 : if (gf_file_exists(opath)) {
600 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[Core] Something went wrong creating temp file path %s, file already exists !\n", opath));
601 0 : gf_free(opath);
602 0 : return NULL;
603 : }
604 0 : GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[Core] Opening new temp file %s\n", opath));
605 0 : res = gf_fopen_ex(opath, "__temp_file", "w+b");
606 0 : if (!res) {
607 0 : gf_free(opath);
608 0 : return NULL;
609 : }
610 0 : gf_register_file_handle(opath, res, GF_TRUE);
611 0 : return res;
612 : }
613 2778 : return gf_file_temp_os(fileName);
614 : }
615 :
616 :
617 : /*enumerate directories*/
618 : GF_EXPORT
619 20106 : GF_Err gf_enum_directory(const char *dir, Bool enum_directory, gf_enum_dir_item enum_dir_fct, void *cbck, const char *filter)
620 : {
621 : #ifdef WIN32
622 : wchar_t item_path[GF_MAX_PATH];
623 : #else
624 : char item_path[GF_MAX_PATH];
625 : #endif
626 : GF_FileEnumInfo file_info;
627 :
628 : #if defined(_WIN32_WCE)
629 : char _path[GF_MAX_PATH];
630 : unsigned short path[GF_MAX_PATH];
631 : unsigned short w_filter[GF_MAX_PATH];
632 : char file[GF_MAX_PATH];
633 : #elif defined(WIN32)
634 : wchar_t path[GF_MAX_PATH], *file;
635 : wchar_t w_filter[GF_MAX_PATH];
636 : char *mbs_file, *mbs_item_path;
637 : char _path[GF_MAX_PATH];
638 : const char* tmpdir;
639 : #else
640 : char path[GF_MAX_PATH], *file;
641 : #endif
642 :
643 : #ifdef WIN32
644 : WIN32_FIND_DATAW FindData;
645 : HANDLE SearchH;
646 : #else
647 : DIR *the_dir;
648 : struct dirent* the_file;
649 : struct stat st;
650 : #endif
651 :
652 20106 : if (!dir || !enum_dir_fct) return GF_BAD_PARAM;
653 :
654 20106 : if (filter && (!strcmp(filter, "*") || !filter[0])) filter=NULL;
655 :
656 : memset(&file_info, 0, sizeof(GF_FileEnumInfo) );
657 :
658 : if (!strcmp(dir, "/")) {
659 : #if defined(WIN32) && !defined(_WIN32_WCE)
660 : u32 len;
661 : char *drives, *volume;
662 : len = GetLogicalDriveStrings(0, NULL);
663 : drives = (char*)gf_malloc(sizeof(char)*(len+1));
664 : drives[0]=0;
665 : GetLogicalDriveStrings(len, drives);
666 : len = (u32) strlen(drives);
667 : volume = drives;
668 : file_info.directory = GF_TRUE;
669 : file_info.drive = GF_TRUE;
670 : while (len) {
671 : enum_dir_fct(cbck, volume, "", &file_info);
672 : volume += len+1;
673 : len = (u32) strlen(volume);
674 : }
675 : gf_free(drives);
676 : return GF_OK;
677 : #elif defined(__SYMBIAN32__)
678 : RFs iFs;
679 : TDriveList aList;
680 : iFs.Connect();
681 : iFs.DriveList(aList);
682 : for (TInt i=0; i<KMaxDrives; i++) {
683 : if (aList[i]) {
684 : char szDrive[10];
685 : TChar aDrive;
686 : iFs.DriveToChar(i, aDrive);
687 : sprintf(szDrive, "%c:", (TUint)aDrive);
688 : enum_dir_fct(cbck, szDrive, "", &file_info);
689 : }
690 : }
691 : iFs.Close();
692 : FlushItemList();
693 : return GF_OK;
694 : #elif defined(GPAC_CONFIG_ANDROID)
695 : dir = getenv("EXTERNAL_STORAGE");
696 : if (!dir) dir = "/sdcard";
697 : #elif defined(GPAC_CONFIG_IOS)
698 : dir = (char *) gf_opts_get_key("General", "iOSDocumentsDir");
699 : #endif
700 : }
701 :
702 :
703 : #if defined (_WIN32_WCE)
704 : switch (dir[strlen(dir) - 1]) {
705 : case '/':
706 : case '\\':
707 : sprintf(_path, "%s*", dir);
708 : break;
709 : default:
710 : sprintf(_path, "%s%c*", dir, GF_PATH_SEPARATOR);
711 : break;
712 : }
713 : CE_CharToWide(_path, path);
714 : CE_CharToWide((char *)filter, w_filter);
715 : #elif defined(WIN32)
716 :
717 : strcpy(_path, dir);
718 : switch (dir[strlen(dir)] - 1) {
719 : case '/':
720 : case '\\':
721 : snprintf(_path, MAX_PATH, "%s*", dir);
722 : break;
723 : default:
724 : snprintf(_path, MAX_PATH, "%s%c*", dir, GF_PATH_SEPARATOR);
725 : break;
726 : }
727 :
728 : tmpdir = _path;
729 : if (gf_utf8_mbstowcs(path, GF_MAX_PATH, &tmpdir) == (size_t)-1) {
730 : GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[Core] Cannot convert %s to UTF16: broken string\n", dir));
731 : return GF_BAD_PARAM;
732 : }
733 : tmpdir = filter;
734 : if (gf_utf8_mbstowcs(w_filter, sizeof(w_filter), &tmpdir) == (size_t)-1) {
735 : GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[Core] Cannot convert %s to UTF16: broken string\n", filter));
736 : return GF_BAD_PARAM;
737 : }
738 :
739 : #else
740 : strcpy(path, dir);
741 20106 : if (path[strlen(path)-1] != '/') strcat(path, "/");
742 : #endif
743 :
744 : #ifdef WIN32
745 : SearchH= FindFirstFileW(path, &FindData);
746 : if (SearchH == INVALID_HANDLE_VALUE) return GF_IO_ERR;
747 :
748 : #if defined (_WIN32_WCE)
749 : _path[strlen(_path)-1] = 0;
750 : #else
751 : path[wcslen(path)-1] = 0;
752 : #endif
753 :
754 : while (SearchH != INVALID_HANDLE_VALUE) {
755 :
756 : #else
757 :
758 20106 : the_dir = opendir(path);
759 20106 : if (the_dir == NULL) {
760 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[Core] Cannot open directory \"%s\" for enumeration: %d\n", path, errno));
761 : return GF_IO_ERR;
762 : }
763 20106 : the_file = readdir(the_dir);
764 106804 : while (the_file) {
765 :
766 : #endif
767 :
768 : memset(&file_info, 0, sizeof(GF_FileEnumInfo) );
769 :
770 :
771 : #if defined (_WIN32_WCE)
772 : if (!wcscmp(FindData.cFileName, _T(".") )) goto next;
773 : if (!wcscmp(FindData.cFileName, _T("..") )) goto next;
774 : #elif defined(WIN32)
775 : if (!wcscmp(FindData.cFileName, L".")) goto next;
776 : if (!wcscmp(FindData.cFileName, L"..")) goto next;
777 : #else
778 79044 : if (!strcmp(the_file->d_name, "..")) goto next;
779 71390 : if (the_file->d_name[0] == '.') goto next;
780 : #endif
781 :
782 : #ifdef WIN32
783 : file_info.directory = (FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? GF_TRUE : GF_FALSE;
784 : if (!enum_directory && file_info.directory) goto next;
785 : if (enum_directory && !file_info.directory) goto next;
786 : #endif
787 :
788 63725 : if (filter) {
789 : #if defined (_WIN32_WCE)
790 : short ext[30];
791 : short *sep = wcsrchr(FindData.cFileName, (wchar_t) '.');
792 : short *found_ext;
793 : u32 ext_len;
794 : if (!sep) goto next;
795 : wcscpy(ext, sep+1);
796 : wcslwr(ext);
797 : ext_len = (u32) wcslen(ext);
798 : found_ext = wcsstr(w_filter, ext);
799 : if (!found_ext) goto next;
800 : if (found_ext[ext_len] && (found_ext[ext_len] != (wchar_t) ';')) goto next;
801 : #elif defined(WIN32)
802 : wchar_t ext[30];
803 : wchar_t *sep = wcsrchr(FindData.cFileName, L'.');
804 : wchar_t *found_ext;
805 : u32 ext_len;
806 : if (!sep) goto next;
807 : wcscpy(ext, sep+1);
808 : wcslwr(ext);
809 : ext_len = (u32) wcslen(ext);
810 : found_ext = wcsstr(w_filter, ext);
811 : if (!found_ext) goto next;
812 : if (found_ext[ext_len] && (found_ext[ext_len] != L';')) goto next;
813 : #else
814 : char ext[30];
815 33650 : char *sep = strrchr(the_file->d_name, '.');
816 : char *found_ext;
817 : u32 ext_len;
818 33650 : if (!sep) goto next;
819 31926 : strcpy(ext, sep+1);
820 31926 : strlwr(ext);
821 31926 : ext_len = (u32) strlen(ext);
822 31926 : found_ext = strstr(filter, ext);
823 31926 : if (!found_ext) goto next;
824 31878 : if (found_ext[ext_len] && (found_ext[ext_len]!=';')) goto next;
825 : #endif
826 : }
827 :
828 : #if defined(WIN32)
829 : file_info.hidden = (FindData.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) ? GF_TRUE : GF_FALSE;
830 : file_info.system = (FindData.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) ? GF_TRUE : GF_FALSE;
831 : file_info.size = MAXDWORD;
832 : file_info.size += 1;
833 : file_info.size *= FindData.nFileSizeHigh;
834 : file_info.size += FindData.nFileSizeLow;
835 : file_info.last_modified = (u64) ((*(LONGLONG *) &FindData.ftLastWriteTime - TIMESPEC_TO_FILETIME_OFFSET) / 10000000);
836 : #endif
837 :
838 : #if defined (_WIN32_WCE)
839 : CE_WideToChar(FindData.cFileName, file);
840 : strcpy(item_path, _path);
841 : strcat(item_path, file);
842 : #elif defined(WIN32)
843 : wcscpy(item_path, path);
844 : wcscat(item_path, FindData.cFileName);
845 : file = FindData.cFileName;
846 : #else
847 : strcpy(item_path, path);
848 : strcat(item_path, the_file->d_name);
849 61953 : GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[Core] Checking file \"%s\" for enum\n", item_path));
850 :
851 61953 : if (stat( item_path, &st ) != 0) goto next;
852 :
853 61953 : file_info.directory = ((st.st_mode & S_IFMT) == S_IFDIR) ? GF_TRUE : GF_FALSE;
854 61953 : if (enum_directory && !file_info.directory) goto next;
855 49256 : if (!enum_directory && file_info.directory) goto next;
856 :
857 48790 : file_info.size = st.st_size;
858 :
859 : {
860 48790 : struct tm _t = * gf_gmtime(& st.st_mtime);
861 48790 : file_info.last_modified = mktime(&_t);
862 : }
863 : file = the_file->d_name;
864 48790 : if (file && file[0]=='.') file_info.hidden = 1;
865 :
866 48790 : if (file_info.directory) {
867 1774 : char * parent_name = strrchr(item_path, '/');
868 1774 : if (!parent_name) {
869 0 : file_info.drive = GF_TRUE;
870 : } else {
871 : struct stat st_parent;
872 1774 : parent_name[0] = 0;
873 1774 : if (stat(item_path, &st_parent) == 0) {
874 1753 : if ((st.st_dev != st_parent.st_dev) || (st.st_ino == st_parent.st_ino) ) {
875 0 : file_info.drive = GF_TRUE;
876 : }
877 : }
878 1774 : parent_name[0] = '/';
879 : }
880 : }
881 : #endif
882 :
883 : #ifdef WIN32
884 : mbs_file = gf_wcs_to_utf8(file);
885 : mbs_item_path = gf_wcs_to_utf8(item_path);
886 : if (!mbs_file || !mbs_item_path)
887 : {
888 : if (mbs_file) gf_free(mbs_file);
889 : if (mbs_item_path) gf_free(mbs_item_path);
890 : return GF_IO_ERR;
891 : }
892 : if (enum_dir_fct(cbck, mbs_file, mbs_item_path, &file_info)) {
893 : BOOL ret = FindClose(SearchH);
894 : if (!ret) {
895 : DWORD err = GetLastError();
896 : GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[core] FindClose() in gf_enum_directory() returned(1) the following error code: %d\n", err));
897 : }
898 : #else
899 48790 : if (enum_dir_fct(cbck, file, item_path, &file_info)) {
900 : #endif
901 : break;
902 : }
903 :
904 : #ifdef WIN32
905 : gf_free(mbs_file);
906 : gf_free(mbs_item_path);
907 : #endif
908 :
909 64820 : next:
910 : #ifdef WIN32
911 : if (!FindNextFileW(SearchH, &FindData)) {
912 : BOOL ret = FindClose(SearchH);
913 : if (!ret) {
914 : DWORD err = GetLastError();
915 : GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[core] FindClose() in gf_enum_directory() returned(2) the following error code: %d\n", err));
916 : }
917 : break;
918 : }
919 : #else
920 66592 : the_file = readdir(the_dir);
921 : #endif
922 : }
923 : #ifndef WIN32
924 20106 : closedir(the_dir);
925 : #endif
926 20106 : return GF_OK;
927 : }
928 :
929 :
930 :
931 : struct __gf_file_io
932 : {
933 : u32 _reserved_null;
934 : void *__this;
935 :
936 : GF_FileIO * (*open)(GF_FileIO *fileio_ref, const char *url, const char *mode, GF_Err *out_error);
937 : GF_Err (*seek)(GF_FileIO *fileio, u64 offset, s32 whence);
938 : u32 (*read)(GF_FileIO *fileio, u8 *buffer, u32 bytes);
939 : u32 (*write)(GF_FileIO *fileio, u8 *buffer, u32 bytes);
940 : s64 (*tell)(GF_FileIO *fileio);
941 : Bool (*eof)(GF_FileIO *fileio);
942 : int (*printf)(GF_FileIO *gfio, const char *format, va_list args);
943 :
944 : char *url;
945 : char *res_url;
946 : void *udta;
947 :
948 : u64 bytes_done, file_size_plus_one;
949 : Bool cache_complete;
950 : u32 bytes_per_sec;
951 :
952 : u32 printf_alloc;
953 : u8* printf_buf;
954 : };
955 :
956 : GF_EXPORT
957 17136211 : Bool gf_fileio_check(FILE *fp)
958 : {
959 : GF_FileIO *fio = (GF_FileIO *)fp;
960 17136211 : if ((fp==stdin) || (fp==stderr) || (fp==stdout))
961 : return GF_FALSE;
962 :
963 16873451 : if (fio && !fio->_reserved_null && (fio->__this==fio))
964 : return GF_TRUE;
965 16869020 : return GF_FALSE;
966 : }
967 :
968 : typedef struct
969 : {
970 : u8 *data;
971 : u32 size;
972 : u32 pos;
973 : } GF_FileIOBlob;
974 :
975 10 : static GF_FileIO *gfio_blob_open(GF_FileIO *fileio_ref, const char *url, const char *mode, GF_Err *out_error)
976 : {
977 10 : GF_FileIOBlob *blob = gf_fileio_get_udta(fileio_ref);
978 10 : if (!url) {
979 10 : gf_free(blob);
980 10 : gf_fileio_del(fileio_ref);
981 : }
982 10 : return NULL;
983 : }
984 :
985 14 : static GF_Err gfio_blob_seek(GF_FileIO *fileio, u64 offset, s32 whence)
986 : {
987 14 : GF_FileIOBlob *blob = gf_fileio_get_udta(fileio);
988 14 : if (whence==SEEK_END) blob->pos = blob->size;
989 11 : else if (whence==SEEK_SET) blob->pos = 0;
990 : else {
991 0 : if (blob->pos + offset > blob->size) return GF_BAD_PARAM;
992 0 : blob->pos += (u32) offset;
993 : }
994 : return GF_OK;
995 : }
996 1587 : static u32 gfio_blob_read(GF_FileIO *fileio, u8 *buffer, u32 bytes)
997 : {
998 1587 : GF_FileIOBlob *blob = gf_fileio_get_udta(fileio);
999 1587 : if (bytes + blob->pos > blob->size)
1000 4 : bytes = blob->size - blob->pos;
1001 1587 : if (bytes) {
1002 1585 : memcpy(buffer, blob->data+blob->pos, bytes);
1003 1585 : blob->pos += bytes;
1004 : }
1005 1587 : return bytes;
1006 : }
1007 29 : static s64 gfio_blob_tell(GF_FileIO *fileio)
1008 : {
1009 29 : GF_FileIOBlob *blob = gf_fileio_get_udta(fileio);
1010 29 : return (s64) blob->pos;
1011 : }
1012 56 : static Bool gfio_blob_eof(GF_FileIO *fileio)
1013 : {
1014 56 : GF_FileIOBlob *blob = gf_fileio_get_udta(fileio);
1015 56 : if (blob->pos==blob->size) return GF_TRUE;
1016 54 : return GF_FALSE;
1017 : }
1018 :
1019 : GF_EXPORT
1020 37 : GF_FileIO *gf_fileio_new(char *url, void *udta,
1021 : gfio_open_proc open,
1022 : gfio_seek_proc seek,
1023 : gfio_read_proc read,
1024 : gfio_write_proc write,
1025 : gfio_tell_proc tell,
1026 : gfio_eof_proc eof,
1027 : gfio_printf_proc printf)
1028 : {
1029 : char szURL[100];
1030 : GF_FileIO *tmp;
1031 :
1032 37 : if (!open || !seek || !tell || !eof) return NULL;
1033 :
1034 37 : if (!write && !read) return NULL;
1035 37 : GF_SAFEALLOC(tmp, GF_FileIO);
1036 37 : if (!tmp) return NULL;
1037 :
1038 37 : tmp->open = open;
1039 37 : tmp->seek = seek;
1040 37 : tmp->write = write;
1041 37 : tmp->read = read;
1042 37 : tmp->tell = tell;
1043 37 : tmp->eof = eof;
1044 37 : tmp->printf = printf;
1045 :
1046 37 : tmp->udta = udta;
1047 37 : if (url)
1048 37 : tmp->res_url = gf_strdup(url);
1049 :
1050 : sprintf(szURL, "gfio://%p", tmp);
1051 37 : tmp->url = gf_strdup(szURL);
1052 37 : tmp->__this = tmp;
1053 37 : return tmp;
1054 : }
1055 :
1056 : GF_EXPORT
1057 17 : const char * gf_fileio_url(GF_FileIO *gfio)
1058 : {
1059 17 : return gfio ? gfio->url : NULL;
1060 : }
1061 :
1062 : GF_EXPORT
1063 23 : const char * gf_fileio_resource_url(GF_FileIO *gfio)
1064 : {
1065 23 : return gfio ? gfio->res_url : NULL;
1066 : }
1067 :
1068 : GF_EXPORT
1069 37 : void gf_fileio_del(GF_FileIO *gfio)
1070 : {
1071 37 : if (!gfio) return;
1072 37 : if (gfio->url) gf_free(gfio->url);
1073 37 : if (gfio->res_url) gf_free(gfio->res_url);
1074 37 : if (gfio->printf_buf) gf_free(gfio->printf_buf);
1075 37 : gf_free(gfio);
1076 : }
1077 :
1078 : GF_EXPORT
1079 5931 : void *gf_fileio_get_udta(GF_FileIO *gfio)
1080 : {
1081 5931 : return gfio ? gfio->udta : NULL;
1082 : }
1083 :
1084 : GF_EXPORT
1085 72 : GF_FileIO *gf_fileio_open_url(GF_FileIO *gfio_ref, const char *url, const char *mode, GF_Err *out_err)
1086 : {
1087 72 : if (!gfio_ref) {
1088 0 : *out_err = GF_BAD_PARAM;
1089 0 : return NULL;
1090 : }
1091 72 : if (!gfio_ref->open) {
1092 0 : *out_err = url ? GF_NOT_SUPPORTED : GF_OK;
1093 0 : return NULL;
1094 : }
1095 72 : return gfio_ref->open(gfio_ref, url, mode, out_err);
1096 : }
1097 :
1098 : static u32 gf_fileio_read(GF_FileIO *gfio, u8 *buffer, u32 nb_bytes)
1099 : {
1100 1836 : if (!gfio) return GF_BAD_PARAM;
1101 1836 : if (gfio->read)
1102 1836 : return gfio->read(gfio, buffer, nb_bytes);
1103 : return 0;
1104 : }
1105 :
1106 : static u32 gf_fileio_write(GF_FileIO *gfio, u8 *buffer, u32 nb_bytes)
1107 : {
1108 208 : if (!gfio) return GF_BAD_PARAM;
1109 208 : if (!gfio->write) return 0;
1110 208 : return gfio->write(gfio, buffer, (u32) nb_bytes);
1111 : }
1112 :
1113 3545 : int gf_fileio_printf(GF_FileIO *gfio, const char *format, va_list args)
1114 : {
1115 : va_list args_copy;
1116 3545 : if (!gfio) return -1;
1117 3545 : if (gfio->printf) return gfio->printf(gfio, format, args);
1118 :
1119 0 : if (!gfio->write) return -1;
1120 :
1121 0 : va_copy(args_copy, args);
1122 0 : u32 len=vsnprintf(NULL, 0, format, args_copy);
1123 0 : va_end(args_copy);
1124 :
1125 0 : if (len>=gfio->printf_alloc) {
1126 0 : gfio->printf_alloc = len+1;
1127 0 : gfio->printf_buf = gf_realloc(gfio->printf_buf, gfio->printf_alloc);
1128 : }
1129 0 : vsnprintf(gfio->printf_buf, len, format, args);
1130 0 : gfio->printf_buf[len] = 0;
1131 0 : return gfio->write(gfio, gfio->printf_buf, len+1);
1132 : }
1133 :
1134 : GF_EXPORT
1135 13 : Bool gf_fileio_write_mode(GF_FileIO *gfio)
1136 : {
1137 13 : return (gfio && gfio->write) ? GF_TRUE : GF_FALSE;
1138 : }
1139 :
1140 : GF_EXPORT
1141 24 : Bool gf_fileio_read_mode(GF_FileIO *gfio)
1142 : {
1143 24 : return (gfio && gfio->read) ? GF_TRUE : GF_FALSE;
1144 : }
1145 :
1146 : GF_EXPORT
1147 92 : GF_FileIO *gf_fileio_from_url(const char *url)
1148 : {
1149 : char szURL[100];
1150 92 : GF_FileIO *ptr=NULL;
1151 92 : if (!url) return NULL;
1152 :
1153 92 : sscanf(url, "gfio://%p", &ptr);
1154 92 : sprintf(szURL, "gfio://%p", ptr);
1155 92 : if (strcmp(url, szURL))
1156 : return NULL;
1157 :
1158 92 : if (ptr && ptr->url && !strcmp(ptr->url, url) ) {
1159 92 : return ptr;
1160 : }
1161 : return NULL;
1162 : }
1163 :
1164 : GF_EXPORT
1165 25 : const char * gf_fileio_translate_url(const char *url)
1166 : {
1167 25 : GF_FileIO *gfio = gf_fileio_from_url(url);
1168 25 : return gfio ? gfio->res_url : NULL;
1169 : }
1170 :
1171 0 : Bool gf_fileio_eof(GF_FileIO *fileio)
1172 : {
1173 101 : if (!fileio || !fileio->tell) return GF_TRUE;
1174 101 : return fileio->eof(fileio);
1175 : }
1176 :
1177 0 : int gf_fileio_flush(GF_FileIO *fileio)
1178 : {
1179 1 : if (!fileio || !fileio->write) return 0;
1180 1 : fileio->write(fileio, NULL, 0);
1181 0 : return 0;
1182 : }
1183 :
1184 : GF_EXPORT
1185 1 : const char *gf_fileio_factory(GF_FileIO *gfio, const char *new_res_url)
1186 : {
1187 : GF_Err e;
1188 1 : if (!gfio || !gfio->open) return NULL;
1189 1 : GF_FileIO *new_res = gfio->open(gfio, new_res_url, "url", &e);
1190 1 : if (e) return NULL;
1191 1 : return gf_fileio_url(new_res);
1192 : }
1193 :
1194 : GF_EXPORT
1195 19 : void gf_fileio_set_stats(GF_FileIO *gfio, u64 bytes_done, u64 file_size, Bool cache_complete, u32 bytes_per_sec)
1196 : {
1197 19 : if (!gfio) return;
1198 19 : gfio->bytes_done = bytes_done;
1199 19 : gfio->file_size_plus_one = file_size ? (1 + file_size) : 0;
1200 19 : gfio->cache_complete = cache_complete;
1201 19 : gfio->bytes_per_sec = bytes_per_sec;
1202 : }
1203 :
1204 :
1205 : GF_EXPORT
1206 6 : Bool gf_fileio_get_stats(GF_FileIO *gfio, u64 *bytes_done, u64 *file_size, Bool *cache_complete, u32 *bytes_per_sec)
1207 : {
1208 6 : if (!gf_fileio_check((FILE *)gfio))
1209 : return GF_FALSE;
1210 :
1211 6 : if (bytes_done) *bytes_done = gfio->bytes_done;
1212 6 : if (file_size) *file_size = gfio->file_size_plus_one ? gfio->file_size_plus_one-1 : 0;
1213 6 : if (cache_complete) *cache_complete = gfio->cache_complete;
1214 6 : if (bytes_per_sec) *bytes_per_sec = gfio->bytes_per_sec;
1215 : return GF_TRUE;
1216 : }
1217 :
1218 : GF_EXPORT
1219 108540 : u64 gf_ftell(FILE *fp)
1220 : {
1221 108540 : if (!fp) return -1;
1222 :
1223 108540 : if (gf_fileio_check(fp)) {
1224 : GF_FileIO *gfio = (GF_FileIO *)fp;
1225 42 : if (!gfio->tell) return -1;
1226 42 : return gfio->tell(gfio);
1227 : }
1228 :
1229 : #if defined(_WIN32_WCE)
1230 : return (u64) ftell(fp);
1231 : #elif defined(GPAC_CONFIG_WIN32) && (defined(__CYGWIN__) || defined(__MINGW32__))
1232 : #if (_FILE_OFFSET_BITS >= 64)
1233 : return (u64) ftello64(fp);
1234 : #else
1235 : return (u64) ftell(fp);
1236 : #endif
1237 : #elif defined(WIN32)
1238 : return (u64) _ftelli64(fp);
1239 : #elif defined(GPAC_CONFIG_LINUX) && !defined(GPAC_CONFIG_ANDROID)
1240 108498 : return (u64) ftello64(fp);
1241 : #elif (defined(GPAC_CONFIG_FREEBSD) || defined(GPAC_CONFIG_DARWIN))
1242 : return (u64) ftello(fp);
1243 : #else
1244 : return (u64) ftell(fp);
1245 : #endif
1246 : }
1247 :
1248 : GF_EXPORT
1249 1627225 : s32 gf_fseek(FILE *fp, s64 offset, s32 whence)
1250 : {
1251 1627225 : if (!fp) return -1;
1252 1627225 : if (gf_fileio_check(fp)) {
1253 : GF_FileIO *gfio = (GF_FileIO *)fp;
1254 77 : if (gfio->seek) {
1255 77 : GF_Err e = gfio->seek(gfio, offset, whence);
1256 77 : if (e) return -1;
1257 77 : return 0;
1258 : }
1259 : return -1;
1260 : }
1261 :
1262 : #if defined(_WIN32_WCE)
1263 : return (u64) fseek(fp, (s32) offset, whence);
1264 : #elif defined(GPAC_CONFIG_WIN32) && defined(__CYGWIN__) /* mingw or cygwin */
1265 : #if (_FILE_OFFSET_BITS >= 64)
1266 : return (u64) fseeko64(fp, offset, whence);
1267 : #else
1268 : return (u64) fseek(fp, (s32) offset, whence);
1269 : #endif
1270 : #elif defined(WIN32)
1271 : return (u64) _fseeki64(fp, offset, whence);
1272 : #elif defined(GPAC_CONFIG_LINUX) && !defined(GPAC_CONFIG_ANDROID)
1273 1627148 : return fseeko64(fp, (off64_t) offset, whence);
1274 : #elif (defined(GPAC_CONFIG_FREEBSD) || defined(GPAC_CONFIG_DARWIN))
1275 : return fseeko(fp, (off_t) offset, whence);
1276 : #else
1277 : return fseek(fp, (s32) offset, whence);
1278 : #endif
1279 : }
1280 :
1281 :
1282 10 : static GF_FileIO *gf_fileio_from_blob(const char *file_name)
1283 : {
1284 : u8 *blob_data;
1285 : u32 blob_size, flags;
1286 : GF_FileIOBlob *gfio_blob;
1287 10 : GF_Err e = gf_blob_get(file_name, &blob_data, &blob_size, &flags);
1288 10 : if (e || !blob_data) return NULL;
1289 10 : gf_blob_release(file_name);
1290 :
1291 10 : if (flags) {
1292 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[Core] Attempt at creating a GFIO object on blob corrupted or in transfer, not supported !"));
1293 : return NULL;
1294 : }
1295 :
1296 10 : GF_SAFEALLOC(gfio_blob, GF_FileIOBlob);
1297 10 : if (!gfio_blob) return NULL;
1298 10 : gfio_blob->data = blob_data;
1299 10 : gfio_blob->size = blob_size;
1300 10 : return gf_fileio_new((char *) file_name, gfio_blob, gfio_blob_open, gfio_blob_seek, gfio_blob_read, NULL, gfio_blob_tell, gfio_blob_eof, NULL);
1301 : }
1302 : GF_EXPORT
1303 62320 : FILE *gf_fopen_ex(const char *file_name, const char *parent_name, const char *mode)
1304 : {
1305 : FILE *res = NULL;
1306 : u32 gfio_type = 0;
1307 :
1308 62320 : if (!file_name) return NULL;
1309 :
1310 62320 : if (!strncmp(file_name, "gmem://", 7)) {
1311 : GF_FileIO *new_gfio;
1312 10 : if (strstr(mode, "w"))
1313 : return NULL;
1314 10 : new_gfio = gf_fileio_from_blob(file_name);
1315 10 : if (new_gfio)
1316 10 : gf_register_file_handle((char *)file_name, (FILE *) new_gfio, GF_FALSE);
1317 : return (FILE *) new_gfio;
1318 :
1319 : }
1320 :
1321 62310 : if (!strncmp(file_name, "gfio://", 7))
1322 : gfio_type = 1;
1323 62291 : else if (parent_name && !strncmp(parent_name, "gfio://", 7))
1324 : gfio_type = 2;
1325 :
1326 : if (gfio_type) {
1327 : GF_FileIO *gfio_ref;
1328 : GF_FileIO *new_gfio;
1329 : GF_Err e;
1330 27 : GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[Core] Open GFIO %s in mode %s\n", file_name, mode));
1331 :
1332 27 : if (gfio_type==1)
1333 19 : gfio_ref = gf_fileio_from_url(file_name);
1334 : else
1335 8 : gfio_ref = gf_fileio_from_url(parent_name);
1336 :
1337 27 : if (!gfio_ref) return NULL;
1338 27 : if (strchr(mode, 'r') && !gf_fileio_read_mode(gfio_ref)) {
1339 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("FileIO %s is not read-enabled and open mode %s was requested\n", file_name, mode));
1340 : return NULL;
1341 : }
1342 27 : if ((strchr(mode, 'w') || strchr(mode, 'a')) && !gf_fileio_write_mode(gfio_ref)) {
1343 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("FileIO %s is not write-enabled and open mode %s was requested\n", file_name, mode));
1344 : return NULL;
1345 : }
1346 27 : new_gfio = gf_fileio_open_url(gfio_ref, file_name, mode, &e);
1347 27 : if (e) {
1348 1 : GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("FileIO %s open in mode %s failed: %s\n", file_name, mode, gf_error_to_string(e)));
1349 : return NULL;
1350 : }
1351 26 : gf_register_file_handle((char *)file_name, (FILE *) new_gfio, GF_FALSE);
1352 26 : return (FILE *) new_gfio;
1353 : }
1354 :
1355 62283 : GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[Core] Open file %s in mode %s\n", file_name, mode));
1356 62283 : if (strchr(mode, 'w')) {
1357 7748 : char *fname = gf_strdup(file_name);
1358 7748 : char *sep = strchr(fname, '/');
1359 7748 : if (!sep) sep = strchr(fname, '\\');
1360 7748 : if (file_name[0] == '/') sep = strchr(fname+1, '/');
1361 7375 : else if (file_name[2] == '\\') sep = strchr(fname+3, '\\');
1362 :
1363 30362 : while (sep) {
1364 : char *n_sep;
1365 22614 : char c = sep[0];
1366 22614 : sep[0] = 0;
1367 22614 : if (!gf_dir_exists(fname)) {
1368 18 : GF_Err e = gf_mkdir(fname);
1369 18 : if (e != GF_OK) {
1370 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[Core] Failed to create directory \"%s\": %s\n", file_name, gf_error_to_string(e) ));
1371 0 : sep[0] = c;
1372 0 : gf_free(fname);
1373 0 : return NULL;
1374 : }
1375 : }
1376 22614 : sep[0] = c;
1377 22614 : n_sep = strchr(sep+1, '/');
1378 22614 : if (!n_sep) n_sep = strchr(sep+1, '\\');
1379 : sep = n_sep;
1380 : }
1381 7748 : gf_free(fname);
1382 : }
1383 :
1384 : #if defined(WIN32)
1385 : wchar_t *wname;
1386 : wchar_t *wmode;
1387 :
1388 : wname = gf_utf8_to_wcs(file_name);
1389 : wmode = gf_utf8_to_wcs(mode);
1390 : if (!wname || !wmode)
1391 : {
1392 : if (wname) gf_free(wname);
1393 : if (wmode) gf_free(wmode);
1394 : return NULL;
1395 : }
1396 : res = _wfsopen(wname, wmode, _SH_DENYNO);
1397 : gf_free(wname);
1398 : gf_free(wmode);
1399 : #elif defined(GPAC_CONFIG_LINUX) && !defined(GPAC_CONFIG_ANDROID)
1400 62283 : res = fopen64(file_name, mode);
1401 : #elif (defined(GPAC_CONFIG_FREEBSD) || defined(GPAC_CONFIG_DARWIN))
1402 : res = fopen(file_name, mode);
1403 : #else
1404 : res = fopen(file_name, mode);
1405 : #endif
1406 :
1407 62283 : if (res) {
1408 55806 : if (!parent_name || strcmp(parent_name, "__temp_file"))
1409 55806 : gf_register_file_handle((char *)file_name, res, GF_FALSE);
1410 :
1411 55806 : GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[Core] file \"%s\" opened in mode \"%s\" - %d file handles\n", file_name, mode, gpac_file_handles));
1412 : } else {
1413 6477 : if (strchr(mode, 'w') || strchr(mode, 'a')) {
1414 : #if defined(WIN32)
1415 : u32 err = GetLastError();
1416 : GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[Core] system failure for file opening of \"%s\" in mode \"%s\": 0x%08x\n", file_name, mode, err));
1417 : #else
1418 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("[Core] system failure for file opening of \"%s\" in mode \"%s\": %d\n", file_name, mode, errno));
1419 : #endif
1420 : }
1421 : }
1422 : return res;
1423 : }
1424 :
1425 : GF_EXPORT
1426 51929 : FILE *gf_fopen(const char *file_name, const char *mode)
1427 : {
1428 51929 : return gf_fopen_ex(file_name, NULL, mode);
1429 : }
1430 :
1431 : GF_EXPORT
1432 58615 : s32 gf_fclose(FILE *file)
1433 : {
1434 58615 : if (!file)
1435 : return 0;
1436 :
1437 58615 : if (gf_unregister_file_handle(file))
1438 : return 0;
1439 58615 : if (gf_fileio_check(file)) {
1440 : GF_Err e;
1441 36 : gf_fileio_open_url((GF_FileIO *) file, NULL, "deref", &e);
1442 36 : if (e) return -1;
1443 27 : return 0;
1444 : }
1445 58579 : return fclose(file);
1446 : }
1447 :
1448 : #if (_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600) && ! defined(_GNU_SOURCE) && !defined(WIN32)
1449 : #define HAVE_STRERROR_R 1
1450 : #endif
1451 :
1452 : GF_EXPORT
1453 2204496 : size_t gf_fwrite(const void *ptr, size_t nb_bytes, FILE *stream)
1454 : {
1455 : size_t result=0;
1456 :
1457 2204496 : if (gf_fileio_check(stream)) {
1458 416 : return(size_t) gf_fileio_write((GF_FileIO *)stream, (u8 *) ptr, (u32) nb_bytes);
1459 : }
1460 :
1461 2204288 : if (ptr)
1462 2204288 : result = fwrite(ptr, 1, nb_bytes, stream);
1463 2204288 : if (result != nb_bytes) {
1464 : #ifdef _WIN32_WCE
1465 : GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("Error writing data: %d blocks to write but %d blocks written\n", nb_bytes, result));
1466 : #else
1467 : #if defined WIN32 && !defined(GPAC_CONFIG_WIN32)
1468 : errno_t errno_save;
1469 : _get_errno(&errno_save);
1470 : #else
1471 0 : int errno_save = errno;
1472 : #endif
1473 : //if (errno_save!=0)
1474 : {
1475 : #ifdef HAVE_STRERROR_R
1476 : #define ERRSTR_BUF_SIZE 256
1477 : char errstr[ERRSTR_BUF_SIZE];
1478 0 : if(strerror_r(errno_save, errstr, ERRSTR_BUF_SIZE) != 0)
1479 : {
1480 0 : strerror_r(0, errstr, ERRSTR_BUF_SIZE);
1481 : }
1482 : #else
1483 : char *errstr = (char*)strerror(errno_save);
1484 : #endif
1485 :
1486 : #ifndef GPAC_DISABLE_LOG
1487 0 : GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("Error writing data (%s): %d blocks to write but %d blocks written\n", errstr, nb_bytes, result));
1488 : #else
1489 : fprintf(stderr, "Error writing data (%s): %d blocks to write but %d blocks written\n", errstr, (u32) nb_bytes, (u32) result);
1490 : #endif
1491 :
1492 : }
1493 : #endif
1494 : }
1495 : return result;
1496 : }
1497 :
1498 : GF_EXPORT
1499 5220048 : size_t gf_fread(void *ptr, size_t nbytes, FILE *stream)
1500 : {
1501 : size_t result;
1502 5220048 : if (gf_fileio_check(stream)) {
1503 522 : return (size_t) gf_fileio_read((GF_FileIO *)stream, ptr, (u32) nbytes);
1504 : }
1505 : result = fread(ptr, 1, nbytes, stream);
1506 : return result;
1507 : }
1508 :
1509 : GF_EXPORT
1510 263325 : char *gf_fgets(char *ptr, size_t size, FILE *stream)
1511 : {
1512 263325 : if (gf_fileio_check(stream)) {
1513 : GF_FileIO *fio = (GF_FileIO *)stream;
1514 : u32 i, read, nb_read=0;
1515 1575 : for (i=0; i<size; i++) {
1516 : u8 buf[1];
1517 : read = (u32) gf_fileio_read(fio, buf, 1);
1518 1575 : if (!read) break;
1519 :
1520 1573 : ptr[nb_read] = buf[0];
1521 1573 : nb_read++;
1522 1573 : if (buf[0]=='\n') break;
1523 : }
1524 120 : if (!nb_read) return NULL;
1525 118 : return ptr;
1526 : }
1527 263205 : return fgets(ptr, (u32) size, stream);
1528 : }
1529 :
1530 : GF_EXPORT
1531 22374 : int gf_fgetc(FILE *stream)
1532 : {
1533 22374 : if (gf_fileio_check(stream)) {
1534 : GF_FileIO *fio = (GF_FileIO *)stream;
1535 : u8 buf[1];
1536 : u32 read = gf_fileio_read(fio, buf, 1);
1537 0 : if (!read) return -1;
1538 0 : return buf[0];
1539 : }
1540 22374 : return fgetc(stream);
1541 : }
1542 :
1543 : GF_EXPORT
1544 17 : int gf_fputc(int val, FILE *stream)
1545 : {
1546 17 : if (gf_fileio_check(stream)) {
1547 : GF_FileIO *fio = (GF_FileIO *)stream;
1548 : u32 write;
1549 : u8 buf[1];
1550 0 : buf[0] = val;
1551 : write = gf_fileio_write(fio, buf, 1);
1552 0 : if (!write) return -1;
1553 0 : return buf[0];
1554 : }
1555 17 : return fputc(val, stream);
1556 : }
1557 :
1558 : GF_EXPORT
1559 25063 : int gf_fputs(const char *buf, FILE *stream)
1560 : {
1561 25063 : if (gf_fileio_check(stream)) {
1562 : GF_FileIO *fio = (GF_FileIO *)stream;
1563 0 : u32 write, len = (u32) strlen(buf);
1564 : write = gf_fileio_write(fio, (u8 *) buf, len);
1565 0 : if (write != len) return -1;
1566 0 : return write;
1567 : }
1568 25063 : return fputs(buf, stream);
1569 : }
1570 :
1571 : GF_EXPORT
1572 3977042 : int gf_fprintf(FILE *stream, const char *format, ...)
1573 : {
1574 : int res;
1575 : va_list args;
1576 3977042 : va_start(args, format);
1577 3977042 : if (gf_fileio_check(stream)) {
1578 3545 : res = gf_fileio_printf((GF_FileIO *)stream, format, args);
1579 : } else {
1580 : res = vfprintf(stream, format, args);
1581 : }
1582 3977042 : va_end(args);
1583 3977042 : return res;
1584 : }
1585 :
1586 : GF_EXPORT
1587 201621 : int gf_fflush(FILE *stream)
1588 : {
1589 201621 : if (gf_fileio_check(stream)) {
1590 : return gf_fileio_flush((GF_FileIO *)stream);
1591 : }
1592 201620 : return fflush(stream);
1593 : }
1594 :
1595 : GF_EXPORT
1596 3390743 : int gf_feof(FILE *stream)
1597 : {
1598 3390743 : if (gf_fileio_check(stream)) {
1599 101 : return gf_fileio_eof((GF_FileIO *)stream) ? 1 : 0;
1600 : }
1601 3390642 : return feof(stream);
1602 : }
1603 :
1604 :
1605 : GF_EXPORT
1606 352 : int gf_ferror(FILE *stream)
1607 : {
1608 352 : if (gf_fileio_check(stream)) {
1609 : return 0;
1610 : }
1611 351 : return ferror(stream);
1612 : }
1613 :
1614 : GF_EXPORT
1615 25457 : u64 gf_fsize(FILE *fp)
1616 : {
1617 : u64 size;
1618 :
1619 25457 : if (gf_fileio_check(fp)) {
1620 : GF_FileIO *gfio = (GF_FileIO *)fp;
1621 22 : if (gfio->file_size_plus_one) {
1622 19 : gf_fseek(fp, 0, SEEK_SET);
1623 19 : return gfio->file_size_plus_one - 1;
1624 : }
1625 : //fall through
1626 : }
1627 25438 : gf_fseek(fp, 0, SEEK_END);
1628 25438 : size = gf_ftell(fp);
1629 25438 : gf_fseek(fp, 0, SEEK_SET);
1630 25438 : return size;
1631 : }
1632 :
1633 : /**
1634 : * Returns a pointer to the start of a filepath basename
1635 : **/
1636 : GF_EXPORT
1637 40388 : char* gf_file_basename(const char* filename)
1638 : {
1639 : char* lastPathPart = NULL;
1640 40388 : if (filename) {
1641 40386 : lastPathPart = strrchr(filename , GF_PATH_SEPARATOR);
1642 : #if GF_PATH_SEPARATOR != '/'
1643 : // windows paths can mix slashes and backslashes
1644 : // so we search for the last slash that occurs after the last backslash
1645 : // if it occurs before it's not relevant
1646 : // if there's no backslashes we search in the whole file path
1647 :
1648 : char* trailingSlash = strrchr(lastPathPart?lastPathPart:filename, '/');
1649 : if (trailingSlash)
1650 : lastPathPart = trailingSlash;
1651 : #endif
1652 40386 : if (!lastPathPart)
1653 : lastPathPart = (char *)filename;
1654 : else
1655 37102 : lastPathPart++;
1656 : }
1657 40388 : return lastPathPart;
1658 : }
1659 :
1660 : /**
1661 : * Returns a pointer to the start of a filepath extension or null
1662 : **/
1663 : GF_EXPORT
1664 32577 : char* gf_file_ext_start(const char* filename)
1665 : {
1666 : char* basename;
1667 :
1668 32577 : if (filename && !strncmp(filename, "gfio://", 7)) {
1669 22 : GF_FileIO *gfio = gf_fileio_from_url(filename);
1670 22 : filename = gf_fileio_resource_url(gfio);
1671 : }
1672 32577 : basename = gf_file_basename(filename);
1673 :
1674 32577 : if (basename) {
1675 32577 : char *ext = strrchr(basename, '.');
1676 32577 : if (ext && !strcmp(ext, ".gz")) {
1677 0 : ext[0] = 0;
1678 0 : char *ext2 = strrchr(basename, '.');
1679 0 : ext[0] = '.';
1680 0 : if (ext2) return ext2;
1681 : }
1682 : return ext;
1683 : }
1684 : return NULL;
1685 : }
1686 :
1687 : GF_EXPORT
1688 78866 : char* gf_url_colon_suffix(const char *path)
1689 : {
1690 78866 : char *sep = strchr(path, ':');
1691 78866 : if (!sep) return NULL;
1692 :
1693 : //handle Z:\ and Z:/
1694 56492 : if ((path[1]==':') && ( (path[2]=='/') || (path[2]=='\\') ) )
1695 0 : return gf_url_colon_suffix(path+2);
1696 :
1697 56492 : if (!strncmp(path, "gfio://", 7) || !strncmp(path, "gmem://", 7)) {
1698 26 : return strchr(path+7, ':');
1699 : }
1700 :
1701 : //handle PROTO://ADD:PORT/
1702 56466 : if ((sep[1]=='/') && (sep[2]=='/')) {
1703 : char *next_colon, *next_slash;
1704 455 : sep++;
1705 : //skip all // (eg PROTO://////////////mytest/)
1706 1820 : while (sep[0]=='/')
1707 910 : sep++;
1708 455 : if (!sep[0]) return NULL;
1709 :
1710 : /*we may now have C:\ or C:/ (eg file://///C:\crazy\ or file://///C:/crazy/)
1711 : if sep[1]==':', then sep[2] is valid (0 or something else), no need to check for len
1712 : */
1713 451 : if ((sep[1]==':') && ( (sep[2]=='/') || (sep[2]=='\\') ) ) {
1714 0 : return gf_url_colon_suffix(sep+2);
1715 : }
1716 : //find closest : or /, if : is before / consider this is a port or an IPv6 address and check next : after /
1717 451 : next_colon = strchr(sep, ':');
1718 451 : next_slash = strchr(sep, '/');
1719 451 : if (next_colon && next_slash && ((next_slash - sep) > (next_colon - sep)) ) {
1720 : const char *last_colon;
1721 : u32 i, port, nb_colons=0, nb_dots=0, nb_non_alnums=0;
1722 238 : next_slash[0] = 0;
1723 238 : last_colon = strrchr(next_colon, ':');
1724 476 : port = atoi(last_colon+1);
1725 1436 : for (i=0; i<strlen(next_colon+1); i++) {
1726 970 : if (next_colon[i+1] == ':') nb_colons++;
1727 955 : else if (next_colon[i+1] == '.') nb_dots++;
1728 : //closing bracket of IPv6
1729 955 : else if (next_colon[i+1] == ']') {}
1730 955 : else if (!isalnum(next_colon[i+1])) {
1731 : nb_non_alnums++;
1732 : break;
1733 : }
1734 : }
1735 238 : next_slash[0] = '/';
1736 : //if no non-alphanum, we must have either a port (ipv4) or extra colons but no dots (ipv6)
1737 238 : if (!nb_non_alnums && (port || (nb_colons && !nb_dots)))
1738 218 : next_colon = strchr(next_slash, ':');
1739 : }
1740 : return next_colon;
1741 : }
1742 : return sep;
1743 : }
|