1 /* vim:expandtab:ts=2 sw=2:
2 */
3 /* Grafx2 - The Ultimate 256-color bitmap paint program
4
5 Copyright owned by various GrafX2 authors, see COPYRIGHT.txt for details.
6
7 Grafx2 is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public License
9 as published by the Free Software Foundation; version 2
10 of the License.
11
12 Grafx2 is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with Grafx2; if not, see <http://www.gnu.org/licenses/>
19 */
20
21 // Fonctions de lecture/ecriture file, gèrent les systèmes big-endian et
22 // little-endian.
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <sys/stat.h>
28 #include <errno.h>
29 #include <fcntl.h>
30 #include <limits.h> // for PATH_MAX (MAX_PATH_CHARACTERS)
31 #ifndef _MSC_VER
32 #include <unistd.h>
33 #else
34 #define strdup _strdup
35 #if _MSC_VER < 1900
36 #define snprintf _snprintf
37 #endif
38 #endif
39
40 #if defined(__amigaos4__) || defined(__AROS__) || defined(__MORPHOS__) || defined(__amigaos__)
41 #include <proto/dos.h>
42 #include <sys/types.h>
43 #include <dirent.h>
44 #elif defined(WIN32)
45 #ifdef _MSC_VER
46 #include <direct.h>
47 #else
48 #include <dirent.h>
49 #endif
50 #include <windows.h>
51 //#include <commdlg.h>
52 #elif defined(__MINT__)
53 #include <mint/osbind.h>
54 #include <mint/sysbind.h>
55 #include <dirent.h>
56 #else
57 #include <dirent.h>
58 #endif
59 #if defined(USE_SDL) || defined(USE_SDL2)
60 #include <SDL_endian.h>
61 #endif
62
63 #include "struct.h"
64 #include "io.h"
65 #include "realpath.h"
66 #include "unicode.h"
67 #include "global.h"
68 #include "gfx2log.h"
69 #include "gfx2mem.h"
70 // for the network browse
71 #include "fileseltools.h"
72
73 // Lit un octet
74 // Renvoie -1 si OK, 0 en cas d'erreur
Read_byte(FILE * file,byte * dest)75 int Read_byte(FILE *file, byte *dest)
76 {
77 return fread(dest, 1, 1, file) == 1;
78 }
79 // Ecrit un octet
80 // Renvoie -1 si OK, 0 en cas d'erreur
Write_byte(FILE * file,byte b)81 int Write_byte(FILE *file, byte b)
82 {
83 return fwrite(&b, 1, 1, file) == 1;
84 }
85 // Lit des octets
86 // Renvoie -1 si OK, 0 en cas d'erreur
Read_bytes(FILE * file,void * dest,size_t size)87 int Read_bytes(FILE *file, void *dest, size_t size)
88 {
89 return fread(dest, 1, size, file) == size;
90 }
91 // Read a line
92 // returns -1 if OK, 0 in case of error
Read_byte_line(FILE * file,char * line,size_t size)93 int Read_byte_line(FILE *file, char *line, size_t size)
94 {
95 return fgets(line, size, file) != NULL;
96 }
97 // Ecrit des octets
98 // Renvoie -1 si OK, 0 en cas d'erreur
Write_bytes(FILE * file,const void * src,size_t size)99 int Write_bytes(FILE *file, const void *src, size_t size)
100 {
101 return fwrite(src, 1, size, file) == size;
102 }
103
104 // Lit un word (little-endian)
105 // Renvoie -1 si OK, 0 en cas d'erreur
Read_word_le(FILE * file,word * dest)106 int Read_word_le(FILE *file, word *dest)
107 {
108 #if defined(USE_SDL) || defined(USE_SDL2)
109 if (fread(dest, 1, sizeof(word), file) != sizeof(word))
110 return 0;
111 #if SDL_BYTEORDER != SDL_LIL_ENDIAN
112 *dest = SDL_Swap16(*dest);
113 #endif
114 return -1;
115 #else
116 byte buffer[2];
117 if (fread(buffer, 1, 2, file) != 2)
118 return 0;
119 *dest = (word)buffer[0] | (word)buffer[1] << 8;
120 return -1;
121 #endif
122 }
123 // Ecrit un word (little-endian)
124 // Renvoie -1 si OK, 0 en cas d'erreur
Write_word_le(FILE * file,word w)125 int Write_word_le(FILE *file, word w)
126 {
127 #if defined(USE_SDL) || defined(USE_SDL2)
128 #if SDL_BYTEORDER != SDL_LIL_ENDIAN
129 w = SDL_Swap16(w);
130 #endif
131 return fwrite(&w, 1, sizeof(word), file) == sizeof(word);
132 #else
133 if (fputc((w >> 0) & 0xff, file) == EOF)
134 return 0;
135 if (fputc((w >> 8) & 0xff, file) == EOF)
136 return 0;
137 return -1;
138 #endif
139 }
140 // Lit un word (big-endian)
141 // Renvoie -1 si OK, 0 en cas d'erreur
Read_word_be(FILE * file,word * dest)142 int Read_word_be(FILE *file, word *dest)
143 {
144 #if defined(USE_SDL) || defined(USE_SDL2)
145 if (fread(dest, 1, sizeof(word), file) != sizeof(word))
146 return 0;
147 #if SDL_BYTEORDER != SDL_BIG_ENDIAN
148 *dest = SDL_Swap16(*dest);
149 #endif
150 return -1;
151 #else
152 byte buffer[2];
153 if (fread(buffer, 1, 2, file) != 2)
154 return 0;
155 *dest = (word)buffer[0] << 8 | (word)buffer[1];
156 return -1;
157 #endif
158 }
159 // Ecrit un word (big-endian)
160 // Renvoie -1 si OK, 0 en cas d'erreur
Write_word_be(FILE * file,word w)161 int Write_word_be(FILE *file, word w)
162 {
163 #if defined(USE_SDL) || defined(USE_SDL2)
164 #if SDL_BYTEORDER != SDL_BIG_ENDIAN
165 w = SDL_Swap16(w);
166 #endif
167 return fwrite(&w, 1, sizeof(word), file) == sizeof(word);
168 #else
169 if (fputc((w >> 8) & 0xff, file) == EOF)
170 return 0;
171 if (fputc((w >> 0) & 0xff, file) == EOF)
172 return 0;
173 return -1;
174 #endif
175 }
176 // Lit un dword (little-endian)
177 // Renvoie -1 si OK, 0 en cas d'erreur
Read_dword_le(FILE * file,dword * dest)178 int Read_dword_le(FILE *file, dword *dest)
179 {
180 #if defined(USE_SDL) || defined(USE_SDL2)
181 if (fread(dest, 1, sizeof(dword), file) != sizeof(dword))
182 return 0;
183 #if SDL_BYTEORDER != SDL_LIL_ENDIAN
184 *dest = SDL_Swap32(*dest);
185 #endif
186 return -1;
187 #else
188 byte buffer[4];
189 if (fread(buffer, 1, 4, file) != 4)
190 return 0;
191 *dest = (dword)buffer[0] | (dword)buffer[1] << 8 | (dword)buffer[2] << 16 | (dword)buffer[3] << 24;
192 return -1;
193 #endif
194 }
195 // Ecrit un dword (little-endian)
196 // Renvoie -1 si OK, 0 en cas d'erreur
Write_dword_le(FILE * file,dword dw)197 int Write_dword_le(FILE *file, dword dw)
198 {
199 #if defined(USE_SDL) || defined(USE_SDL2)
200 #if SDL_BYTEORDER != SDL_LIL_ENDIAN
201 dw = SDL_Swap32(dw);
202 #endif
203 return fwrite(&dw, 1, sizeof(dword), file) == sizeof(dword);
204 #else
205 if (fputc((dw >> 0) & 0xff, file) == EOF)
206 return 0;
207 if (fputc((dw >> 8) & 0xff, file) == EOF)
208 return 0;
209 if (fputc((dw >> 16) & 0xff, file) == EOF)
210 return 0;
211 if (fputc((dw >> 24) & 0xff, file) == EOF)
212 return 0;
213 return -1;
214 #endif
215 }
216
217 // Lit un dword (big-endian)
218 // Renvoie -1 si OK, 0 en cas d'erreur
Read_dword_be(FILE * file,dword * dest)219 int Read_dword_be(FILE *file, dword *dest)
220 {
221 #if defined(USE_SDL) || defined(USE_SDL2)
222 if (fread(dest, 1, sizeof(dword), file) != sizeof(dword))
223 return 0;
224 #if SDL_BYTEORDER != SDL_BIG_ENDIAN
225 *dest = SDL_Swap32(*dest);
226 #endif
227 return -1;
228 #else
229 byte buffer[4];
230 if (fread(buffer, 1, 4, file) != 4)
231 return 0;
232 *dest = (dword)buffer[0] << 24 | (dword)buffer[1] << 16 | (dword)buffer[2] << 8 | (dword)buffer[3];
233 return -1;
234 #endif
235 }
236 // Ecrit un dword (big-endian)
237 // Renvoie -1 si OK, 0 en cas d'erreur
Write_dword_be(FILE * file,dword dw)238 int Write_dword_be(FILE *file, dword dw)
239 {
240 #if defined(USE_SDL) || defined(USE_SDL2)
241 #if SDL_BYTEORDER != SDL_BIG_ENDIAN
242 dw = SDL_Swap32(dw);
243 #endif
244 return fwrite(&dw, 1, sizeof(dword), file) == sizeof(dword);
245 #else
246 if (fputc((dw >> 24) & 0xff, file) == EOF)
247 return 0;
248 if (fputc((dw >> 16) & 0xff, file) == EOF)
249 return 0;
250 if (fputc((dw >> 8) & 0xff, file) == EOF)
251 return 0;
252 if (fputc((dw >> 0) & 0xff, file) == EOF)
253 return 0;
254 return -1;
255 #endif
256 }
257
258 // Détermine la position du dernier '/' ou '\\' dans une chaine,
259 // typiquement pour séparer le nom de file d'un chemin.
260 // Attention, sous Windows, il faut s'attendre aux deux car
261 // par exemple un programme lancé sous GDB aura comme argv[0]:
262 // d:\Data\C\GFX2\grafx2/grafx2.exe
Find_last_separator(const char * str)263 char * Find_last_separator(const char * str)
264 {
265 const char * position = NULL;
266
267 if (str == NULL)
268 return NULL;
269 for (; *str != '\0'; str++)
270 if (*str == PATH_SEPARATOR[0]
271 #if defined(__WIN32__) || defined(WIN32)
272 || *str == '/'
273 #elif __AROS__
274 || *str == ':'
275 #endif
276 )
277 position = str;
278 return (char *)position;
279 }
280
Find_last_separator_unicode(const word * str)281 word * Find_last_separator_unicode(const word * str)
282 {
283 const word * position = NULL;
284 for (; *str != 0; str++)
285 if (*str == (byte)PATH_SEPARATOR[0]
286 #if defined(__WIN32__) || defined(WIN32)
287 || *str == '/'
288 #elif __AROS__
289 || *str == ':'
290 #endif
291 )
292 position = str;
293 return (word *)position;
294 }
295
Filepath_append_to_dir(const char * dir,const char * filename)296 char * Filepath_append_to_dir(const char * dir, const char * filename)
297 {
298 char * path;
299 size_t len = dir == NULL ? 0 : strlen(dir);
300 if (len == 0) // no directory
301 return strdup(filename);
302 if (dir[len-1] == PATH_SEPARATOR[0]
303 #if defined(__WIN32__) || defined(WIN32)
304 || dir[len-1] == '/'
305 #elif __AROS__
306 || dir[len-1] == ':'
307 #endif
308 )
309 {
310 len += strlen(filename) + 1;
311 path = GFX2_malloc(len);
312 if (path == NULL)
313 return NULL;
314 snprintf(path, len, "%s%s", dir, filename);
315 }
316 else
317 {
318 // need to add a path separator
319 len += strlen(PATH_SEPARATOR) + strlen(filename) + 1;
320 path = GFX2_malloc(len);
321 if (path == NULL)
322 return NULL;
323 snprintf(path, len, "%s%s%s", dir, PATH_SEPARATOR, filename);
324 }
325 return path;
326 }
327
328 // Récupère la partie "nom de file seul" d'un chemin
Extract_filename(const char * source)329 char * Extract_filename(const char *source)
330 {
331 const char * position = Find_last_separator(source);
332
333 if (position)
334 return strdup(position + 1);
335 else
336 return strdup(source);
337 }
338
339 // Récupère la partie "répertoire+/" d'un chemin.
Extract_path(const char * source)340 char * Extract_path(const char *source)
341 {
342 char * position;
343 char * path;
344
345 path = Realpath(source);
346 if (path == NULL)
347 {
348 GFX2_Log(GFX2_ERROR, "Realpath(\"%s\") failed !\n", source);
349 return NULL;
350 }
351 position = Find_last_separator(path);
352 if (position)
353 position[1] = '\0';
354 else
355 {
356 size_t len = strlen(path);
357 char * tmp = realloc(path, len + strlen(PATH_SEPARATOR) + 1);
358 if (tmp != NULL)
359 {
360 path = tmp;
361 strcpy(path + len, PATH_SEPARATOR);
362 }
363 else
364 {
365 GFX2_Log(GFX2_ERROR, "Extract_path(): Failed to realloc %lu bytes\n",
366 (unsigned long)(len + strlen(PATH_SEPARATOR) + 1));
367 }
368 }
369 return path;
370 }
371
372 ///
373 /// Appends a file or directory name to an existing directory name.
374 /// As a special case, when the new item is equal to PARENT_DIR, this
375 /// will remove the rightmost directory name.
376 /// reverse_path is optional, if it's non-null, the function will
377 /// write there :
378 /// - if filename is ".." : The name of eliminated directory/file
379 /// - else: ".."
Append_path(char * path,const char * filename,char * reverse_path)380 void Append_path(char *path, const char *filename, char *reverse_path)
381 {
382 // Parent
383 if (!strcmp(filename, PARENT_DIR))
384 {
385 // Going up one directory
386 long len;
387 char * separator_pos;
388
389 // Remove trailing slash
390 len=strlen(path);
391 if (len && (!strcmp(path+len-1,PATH_SEPARATOR)
392 #ifdef __WIN32__
393 || path[len-1]=='/'
394 #endif
395 ))
396 path[len-1]='\0';
397
398 separator_pos=Find_last_separator(path);
399 if (separator_pos)
400 {
401 if (reverse_path)
402 strcpy(reverse_path, separator_pos+1);
403 #if defined(__AROS__)
404 // Don't strip away the colon
405 if (*separator_pos == ':') *(separator_pos+1)='\0';
406 else *separator_pos='\0';
407 #else
408 *separator_pos='\0';
409 #endif
410 }
411 else
412 {
413 if (reverse_path)
414 strcpy(reverse_path, path);
415 path[0]='\0';
416 }
417 #if defined(__WIN32__)
418 // Roots of drives need a pending antislash
419 if (path[0]!='\0' && path[1]==':' && path[2]=='\0')
420 {
421 strcat(path, PATH_SEPARATOR);
422 }
423 #endif
424 }
425 else
426 // Sub-directory
427 {
428 long len;
429 // Add trailing slash if needed
430 len=strlen(path);
431 if (len && (strcmp(path+len-1,PATH_SEPARATOR)
432 #ifdef __WIN32__
433 && path[len-1]!='/'
434 #elif __AROS__
435 && path[len-1]!=':' // To avoid paths like volume:/dir
436 #endif
437 ))
438 {
439 strcpy(path+len, PATH_SEPARATOR);
440 len+=strlen(PATH_SEPARATOR);
441 }
442 strcat(path, filename);
443
444 if (reverse_path)
445 strcpy(reverse_path, PARENT_DIR);
446 }
447 }
448
Position_last_dot(const char * fname)449 int Position_last_dot(const char * fname)
450 {
451 int pos_last_dot = -1;
452 int c = 0;
453
454 for (c = 0; fname[c] != '\0'; c++)
455 if (fname[c] == '.')
456 pos_last_dot = c;
457 return pos_last_dot;
458 }
459
Position_last_dot_unicode(const word * fname)460 int Position_last_dot_unicode(const word * fname)
461 {
462 int pos_last_dot = -1;
463 int c = 0;
464
465 for (c = 0; fname[c] != '\0'; c++)
466 if (fname[c] == '.')
467 pos_last_dot = c;
468 return pos_last_dot;
469 }
470
File_exists(const char * fname)471 int File_exists(const char * fname)
472 // Détermine si un file passé en paramètre existe ou non dans le
473 // répertoire courant.
474 {
475 #if defined(WIN32)
476 return (INVALID_FILE_ATTRIBUTES == GetFileAttributesA(fname)) ? 0 : 1;
477 #else
478 struct stat buf;
479 int result;
480
481 result=stat(fname,&buf);
482 if (result!=0)
483 return(errno!=ENOENT);
484 else
485 return 1;
486 #endif
487 }
488
Directory_exists(const char * directory)489 int Directory_exists(const char * directory)
490 // Détermine si un répertoire passé en paramètre existe ou non dans le
491 // répertoire courant.
492 {
493 #if defined(WIN32)
494 DWORD attr = GetFileAttributesA(directory);
495 if (attr == INVALID_FILE_ATTRIBUTES)
496 return 0;
497 return (attr & FILE_ATTRIBUTE_DIRECTORY) ? 1 : 0;
498 #else
499 DIR* entry; // Structure de lecture des éléments
500
501 if (strcmp(directory,PARENT_DIR)==0)
502 return 1;
503 else
504 {
505 // On va chercher si le répertoire existe à l'aide d'un Opendir. S'il
506 // renvoie NULL c'est que le répertoire n'est pas accessible...
507
508 entry=opendir(directory);
509 if (entry==NULL)
510 return 0;
511 else
512 {
513 closedir(entry);
514 return 1;
515 }
516 }
517 #endif
518 }
519
Directory_create(const char * directory)520 int Directory_create(const char * directory)
521 {
522 #if defined(__WIN32__) || defined(WIN32)
523 return CreateDirectoryA(directory, NULL) ? 0 : -1;
524 #else
525 return mkdir(directory, S_IRUSR|S_IWUSR|S_IXUSR);
526 #endif
527 }
528
529 /// Check if a file or directory is hidden.
File_is_hidden(const char * fname,const char * full_name)530 int File_is_hidden(const char *fname, const char *full_name)
531 {
532 #if defined(__amigaos4__) || defined(__AROS__) || defined(__MORPHOS__) || defined(__amigaos__) || defined(__MINT__)|| defined(__SWITCH__)
533 // False (unable to determine, or irrelevent for platform)
534 (void)fname;//unused
535 (void)full_name;//unused
536 return 0;
537 #elif defined(__WIN32__)
538 unsigned long att;
539 if (full_name!=NULL)
540 att = GetFileAttributesA(full_name);
541 else
542 att = GetFileAttributesA(fname);
543 if (att==INVALID_FILE_ATTRIBUTES)
544 return 0;
545 return (att&FILE_ATTRIBUTE_HIDDEN)?1:0;
546 #else
547 (void)full_name;//unused
548 // On linux/unix (default), files are considered hidden if their name
549 // begins with a .
550 // As a special case, we'll consider 'parent directory' (..) never hidden.
551 return fname[0]=='.' && strcmp(fname, PARENT_DIR);
552 #endif
553 }
554
555 // File size in bytes
File_length(const char * fname)556 unsigned long File_length(const char * fname)
557 {
558 #if defined(WIN32)
559 WIN32_FILE_ATTRIBUTE_DATA infos;
560 if (GetFileAttributesExA(fname, GetFileExInfoStandard, &infos))
561 {
562 return (unsigned long)(((DWORD64)infos.nFileSizeHigh << 32) + (DWORD64)infos.nFileSizeLow);
563 }
564 else
565 return 0;
566 #else
567 struct stat infos_fichier;
568 if (stat(fname,&infos_fichier))
569 return 0;
570 return infos_fichier.st_size;
571 #endif
572 }
573
File_length_file(FILE * file)574 unsigned long File_length_file(FILE * file)
575 {
576 #if defined(WIN32)
577 // revert to old school way of finding file size
578 long offset_backup;
579 long file_length;
580 offset_backup = ftell(file);
581 if (offset_backup < 0)
582 return 0;
583 if (fseek(file, 0, SEEK_END) < 0)
584 return 0;
585 file_length = ftell(file);
586 if (file_length < 0)
587 file_length = 0;
588 fseek(file, offset_backup, SEEK_SET);
589 return (unsigned long)file_length;
590 #else
591 struct stat infos_fichier;
592 if (fstat(fileno(file),&infos_fichier))
593 return 0;
594 return infos_fichier.st_size;
595 #endif
596 }
597
For_each_file(const char * directory_name,void Callback (const char *,const char *))598 void For_each_file(const char * directory_name, void Callback(const char *, const char *))
599 {
600 #if defined(WIN32)
601 WIN32_FIND_DATAA fd;
602 char * full_filename;
603 char * search_string;
604 HANDLE h;
605
606 full_filename = Realpath(directory_name);
607 search_string = Filepath_append_to_dir((full_filename != NULL) ? full_filename : directory_name, "*");
608 free(full_filename);
609 h = FindFirstFileA(search_string, &fd);
610 free(search_string);
611 if (h != INVALID_HANDLE_VALUE)
612 {
613 do
614 {
615 if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
616 continue;
617 full_filename = Filepath_append_to_dir(directory_name, fd.cFileName);
618 Callback(full_filename, fd.cFileName);
619 free(full_filename);
620 }
621 while (FindNextFileA(h, &fd));
622 FindClose(h);
623 }
624 #else
625 // directory traversal
626 DIR* current_directory;
627 struct dirent* entry; // directory entry
628
629 current_directory = opendir(directory_name);
630 if(current_directory == NULL)
631 return; // Invalid directory
632 while ((entry = readdir(current_directory)) != NULL)
633 {
634 char * full_filename;
635 struct stat st;
636
637 full_filename = Filepath_append_to_dir(directory_name, entry->d_name);
638 // d_name is the only field you can count on in all POSIX systems.
639 // Also we need to call stat() in order to follow symbolic links
640 if (stat(full_filename, &st) < 0)
641 GFX2_Log(GFX2_WARNING, "stat(\"%s\") failed\n", full_filename);
642 else
643 {
644 if (S_ISREG(st.st_mode))
645 Callback(full_filename, entry->d_name);
646 }
647 free(full_filename);
648 }
649 closedir(current_directory);
650 #endif
651 }
652
653 /// Scans a directory, calls Callback for each file or directory in it,
For_each_directory_entry(const char * directory_name,void * pdata,T_File_dir_cb Callback)654 void For_each_directory_entry(const char * directory_name, void * pdata, T_File_dir_cb Callback)
655 {
656 #if defined(WIN32)
657 WIN32_FIND_DATAW fd;
658 size_t len;
659 word * search_string;
660 HANDLE h;
661
662 len = strlen(directory_name) + 3;
663 search_string = (word *)GFX2_malloc(sizeof(word) * len);
664 if (search_string == NULL)
665 return;
666 Unicode_char_strlcpy(search_string, directory_name, len);
667 Unicode_char_strlcat(search_string, "\\*", len);
668 h = FindFirstFileW((WCHAR *)search_string, &fd);
669 free(search_string);
670 if (h == INVALID_HANDLE_VALUE)
671 {
672 GFX2_Log(GFX2_ERROR, "FindFirstFileW failed in %s\n", directory_name);
673 return;
674 }
675 do
676 {
677 int i;
678 char * short_filename;
679 const WCHAR * src;
680
681 src = (fd.cAlternateFileName[0] != 0) ? fd.cAlternateFileName : fd.cFileName;
682 short_filename = GFX2_malloc(lstrlenW(src) + 1);
683 if (short_filename == NULL)
684 continue;
685 for (i = 0; src[i] != 0; i++)
686 {
687 if (src[i] >= 256)
688 {
689 GFX2_Log(GFX2_WARNING, "Non latin1 character in translation : \\u%04x\n", (int)src[i]);
690 short_filename[i] = '?';
691 }
692 else
693 short_filename[i] = (char)src[i];
694 }
695 short_filename[i] = '\0';
696 Callback(
697 pdata,
698 short_filename,
699 (const word *)fd.cFileName,
700 (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? 0 : 1,
701 (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? 1 : 0,
702 (fd.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) ? 1 : 0
703 );
704 free(short_filename);
705 }
706 while (FindNextFileW(h, &fd));
707
708 FindClose(h);
709 #else
710 DIR* current_directory; // current directory
711 struct dirent* entry; // directory entry struct
712
713 current_directory = opendir(directory_name);
714 if(current_directory == NULL)
715 return; // Invalid directory
716
717 while ((entry = readdir(current_directory)) != NULL)
718 {
719 word * unicode_filename = NULL;
720 char * full_filename;
721 struct stat st;
722 #ifdef ENABLE_FILENAMES_ICONV
723 if (cd_utf16 != (iconv_t)-1)
724 {
725 char * input = entry->d_name;
726 size_t inbytesleft = strlen(entry->d_name);
727 char * output;
728 size_t outbytesleft;
729 size_t r;
730
731 unicode_filename = GFX2_malloc(sizeof(word) * (inbytesleft + 1));
732 if (unicode_filename != NULL)
733 {
734 output = (char *)unicode_filename;
735 outbytesleft = sizeof(word) * inbytesleft;
736 r = iconv(cd_utf16, &input, &inbytesleft, &output, &outbytesleft);
737 if (r != (size_t)-1)
738 {
739 output[0] = '\0';
740 output[1] = '\0';
741 }
742 else
743 {
744 free(unicode_filename);
745 unicode_filename = NULL;
746 }
747 }
748 }
749 #endif
750 full_filename = Filepath_append_to_dir(directory_name, entry->d_name);
751 if (stat(full_filename, &st) < 0)
752 GFX2_Log(GFX2_WARNING, "stat(\"%s\") failed\n", full_filename);
753 else
754 {
755 Callback(
756 pdata,
757 entry->d_name,
758 unicode_filename,
759 S_ISREG(st.st_mode),
760 S_ISDIR(st.st_mode),
761 File_is_hidden(entry->d_name, full_filename));
762 }
763 free(full_filename);
764 free(unicode_filename);
765 }
766 closedir(current_directory);
767 #endif
768 }
769
770
771 /**
772 * Convert a file name to unicode characters
773 *
774 * If the parametter is null, the buffer is malloc'ed
775 *
776 * @param filename_unicode the output buffer of MAX_PATH_CHARACTERS wide characters
777 * @param filename the input file name
778 * @param directory the input file directory
779 * @return NULL if no conversion has taken place.
780 * @return filename_unicode if the unicode filename has been retrieved
781 */
Get_Unicode_Filename(word * filename_unicode,const char * filename,const char * directory)782 word * Get_Unicode_Filename(word * filename_unicode, const char * filename, const char * directory)
783 {
784 #if defined(WIN32)
785 int i = 0;
786 DWORD len;
787 WCHAR * shortPath;
788 WCHAR * longPath;
789 WCHAR * sep;
790
791 shortPath = (WCHAR *)GFX2_malloc(sizeof(WCHAR) * (strlen(filename) + strlen(directory) + 2));
792 if (shortPath == NULL)
793 return NULL;
794 // copy the full path to a wide character buffer :
795 while (directory[0] != '\0')
796 shortPath[i++] = *directory++;
797 if (i > 0 && shortPath[i-1] != '\\') // add path separator only if it is missing
798 shortPath[i++] = '\\';
799 while (filename[0] != '\0')
800 shortPath[i++] = *filename++;
801 shortPath[i++] = 0;
802 len = GetLongPathNameW(shortPath, NULL, 0);
803 if (len == 0)
804 {
805 GFX2_Log(GFX2_ERROR, "GetLongPathNameW(%s\\%s, NULL, 0) returned 0\n", directory, filename);
806 free(shortPath);
807 return NULL;
808 }
809 longPath = (WCHAR *)GFX2_malloc(len * sizeof(WCHAR));
810 if (longPath == NULL)
811 {
812 free(shortPath);
813 return NULL;
814 }
815 if (GetLongPathNameW(shortPath, longPath, len) == 0)
816 {
817 GFX2_Log(GFX2_ERROR, "GetLongPathNameW(%s\\%s, %p, %u) returned 0\n", directory, filename, longPath, len);
818 free(longPath);
819 free(shortPath);
820 return NULL;
821 }
822 free(shortPath);
823 sep = wcsrchr(longPath, '\\');
824 if (sep == NULL)
825 {
826 if (filename_unicode == NULL)
827 return (word *)longPath;
828 memcpy(filename_unicode, longPath, sizeof(word) * len);
829 }
830 else
831 {
832 sep++;
833 len = wcslen(sep) + 1;
834 if (filename_unicode == NULL)
835 filename_unicode = (word *)GFX2_malloc(sizeof(word) * len);
836 if (filename_unicode != NULL)
837 memcpy(filename_unicode, sep, sizeof(word) * len);
838 }
839 free(longPath);
840 return filename_unicode;
841 #elif defined(ENABLE_FILENAMES_ICONV)
842 int allocated_memory = 0;
843 char * input = (char *)filename;
844 size_t inbytesleft = strlen(filename);
845 char * output;
846 size_t outbytesleft;
847 size_t r;
848
849 (void)directory; // unused
850 if (cd_utf16 == (iconv_t)-1)
851 return NULL;
852 if (filename_unicode == NULL)
853 {
854 filename_unicode = GFX2_malloc(sizeof(word) * (inbytesleft + 1));
855 if (filename_unicode == NULL)
856 return NULL;
857 allocated_memory = 1;
858 outbytesleft = inbytesleft * 2;
859 }
860 else
861 outbytesleft = (MAX_PATH_CHARACTERS - 1) * 2;
862 output = (char *)filename_unicode;
863
864 r = iconv(cd_utf16, &input, &inbytesleft, &output, &outbytesleft);
865 if (r != (size_t)-1)
866 {
867 output[0] = '\0';
868 output[1] = '\0';
869 return filename_unicode;
870 }
871 if (allocated_memory)
872 free(filename_unicode);
873 return NULL;
874 #else
875 (void)filename_unicode;
876 (void)filename;
877 (void)directory;
878 // not implemented
879 return NULL;
880 #endif
881 }
882
883 /// Lock file used to prevent several instances of grafx2 from harming each others' backups
884 #ifdef WIN32
885 HANDLE Lock_file_handle = INVALID_HANDLE_VALUE;
886 #else
887 int Lock_file_handle = -1;
888 #endif
889
890 #define GFX2_LOCK_FILENAME "gfx2.lck"
891
Create_lock_file(const char * file_directory)892 byte Create_lock_file(const char *file_directory)
893 {
894 #if defined (__amigaos__)||(__AROS__)||(__ANDROID__)
895 #warning "Missing code for your platform, please check and correct!"
896 #elif defined(__SWITCH__)
897 // The switch can only run one application at a time, so we don't do anything special here
898 #else
899 char * lock_filename;
900
901 #ifdef GCWZERO
902 lock_filename = Filepath_append_to_dir("/media/home/.grafx2/", GFX2_LOCK_FILENAME);
903 #else
904 lock_filename = Filepath_append_to_dir(file_directory, GFX2_LOCK_FILENAME);
905 #endif
906
907 #ifdef WIN32
908 // Windowzy method for creating a lock file
909 Lock_file_handle = CreateFileA(
910 lock_filename,
911 GENERIC_WRITE,
912 0, // No sharing
913 NULL,
914 OPEN_ALWAYS,
915 FILE_ATTRIBUTE_NORMAL,
916 NULL);
917 free(lock_filename);
918 if (Lock_file_handle == INVALID_HANDLE_VALUE)
919 {
920 return -1;
921 }
922 #else
923 // Unixy method for lock file
924 Lock_file_handle = open(lock_filename,O_WRONLY|O_CREAT,S_IRUSR|S_IWUSR);
925 free(lock_filename);
926 if (Lock_file_handle == -1)
927 {
928 // Usually write-protected media
929 return -1;
930 }
931 if (lockf(Lock_file_handle, F_TLOCK, 0)==-1)
932 {
933 close(Lock_file_handle);
934 // Usually write-protected media
935 return -1;
936 }
937 #endif
938 #endif // __amigaos__ or __AROS__
939 return 0;
940 }
941
Release_lock_file(const char * file_directory)942 void Release_lock_file(const char *file_directory)
943 {
944 char * lock_filename;
945
946 #ifdef WIN32
947 if (Lock_file_handle != INVALID_HANDLE_VALUE)
948 {
949 CloseHandle(Lock_file_handle);
950 }
951 #else
952 if (Lock_file_handle != -1)
953 {
954 close(Lock_file_handle);
955 Lock_file_handle = -1;
956 }
957 #endif
958
959 // Actual deletion
960 #ifdef GCWZERO
961 lock_filename = Filepath_append_to_dir("/media/home/.grafx2/", GFX2_LOCK_FILENAME);
962 #else
963 lock_filename = Filepath_append_to_dir(file_directory, GFX2_LOCK_FILENAME);
964 #endif
965 Remove_path(lock_filename);
966 free(lock_filename);
967 }
968
Get_current_directory(char * buf,word ** unicode,size_t size)969 char * Get_current_directory(char * buf, word * * unicode, size_t size)
970 {
971 #if defined(__MINT__)
972 if (buf == NULL)
973 {
974 buf = GFX2_malloc(MAX_PATH_CHARACTERS);
975 if (buf == NULL)
976 return NULL;
977 }
978 buf[0] = 'A'+Dgetdrv();
979 buf[1] = ':';
980 buf[2] = '\\';
981 Dgetpath(buf+3,0);
982 strcat(buf,PATH_SEPARATOR);
983
984 if (unicode != NULL)
985 *unicode = NULL; // no unicode support
986
987 return buf;
988 #elif defined(WIN32)
989 WCHAR * cur_dir, * short_dir;
990 size_t size_cur, size_short;
991 size_t i;
992
993 // first get the current directory in unicode
994 size_cur = (size_t)GetCurrentDirectoryW(0, NULL);
995 if (size_cur == 0)
996 {
997 GFX2_Log(GFX2_ERROR, "GetCurrentDirectoryW(0, NULL) failed !\n");
998 return NULL;
999 }
1000 cur_dir = (WCHAR *)GFX2_malloc(sizeof(WCHAR) * size_cur);
1001 if (cur_dir == NULL)
1002 return NULL;
1003 if (GetCurrentDirectoryW(size_cur, cur_dir) == 0)
1004 {
1005 GFX2_Log(GFX2_ERROR, "GetCurrentDirectoryW(%u, %p) failed !\n", (unsigned)size_cur, cur_dir);
1006 return NULL;
1007 }
1008
1009 // convert to "short" path (ie C:\PROGRA~1\...)
1010 size_short = (size_t)GetShortPathNameW(cur_dir, NULL, 0);
1011 if (size_short == 0)
1012 {
1013 GFX2_Log(GFX2_ERROR, "GetShortPathNameW(%p, NULL, 0) failed !\n", cur_dir);
1014 free(cur_dir);
1015 return NULL;
1016 }
1017 short_dir = (WCHAR *)GFX2_malloc(sizeof(WCHAR) * size_short);
1018 if (short_dir == NULL)
1019 {
1020 free(cur_dir);
1021 return NULL;
1022 }
1023 if (GetShortPathNameW(cur_dir, short_dir, size_short) == 0)
1024 {
1025 GFX2_Log(GFX2_ERROR, "GetShortPathNameW(%p, %p, %u) failed !\n", cur_dir, short_dir, (unsigned)size_short);
1026 free(cur_dir);
1027 free(short_dir);
1028 return NULL;
1029 }
1030
1031 // now copy / return the path
1032 if (buf == NULL)
1033 {
1034 size = size_short;
1035 buf = (char *)GFX2_malloc(size);
1036 if (buf == NULL)
1037 {
1038 free(cur_dir);
1039 free(short_dir);
1040 return NULL;
1041 }
1042 }
1043 for (i = 0; i < (size - 1) && short_dir[i]; i++)
1044 buf[i] = (char)short_dir[i];
1045 buf[i] = '\0';
1046 free(short_dir);
1047
1048 if (unicode != NULL)
1049 {
1050 WCHAR * long_dir;
1051 size_t size_long;
1052
1053 *unicode = NULL;
1054 // convert to "long" path for display
1055 size_long = (size_t)GetLongPathNameW(cur_dir, NULL, 0);
1056 if (size_long == 0)
1057 GFX2_Log(GFX2_ERROR, "GetLongPathNameW(%p, NULL, 0) failed !\n", cur_dir);
1058 else
1059 {
1060 long_dir = (WCHAR *)GFX2_malloc(sizeof(WCHAR) * size_long);
1061 if (long_dir != NULL)
1062 {
1063 if (GetLongPathNameW(cur_dir, long_dir, size_long) == 0)
1064 {
1065 GFX2_Log(GFX2_ERROR, "GetLongPathNameW(%p, %p, %u) failed !\n", cur_dir, long_dir, (unsigned)size_long);
1066 free(long_dir);
1067 }
1068 else
1069 *unicode = (word *)long_dir;
1070 }
1071 }
1072 }
1073 free(cur_dir);
1074 return buf;
1075 #else
1076 char * ret = getcwd(buf, size);
1077 if (ret == NULL)
1078 GFX2_Log(GFX2_ERROR, "getcwd(%p, %lu) failed !\n", buf, (unsigned long)size);
1079 #ifdef ENABLE_FILENAMES_ICONV
1080 if (ret != NULL && unicode != NULL)
1081 {
1082 char * input = ret;
1083 size_t inbytesleft = strlen(ret);
1084 word * buf_unicode = GFX2_malloc((inbytesleft + 1) * 2);
1085 char * output = (char *)buf_unicode;
1086 size_t outbytesleft = 2 * inbytesleft;
1087 if (cd_utf16 != (iconv_t)-1 && buf_unicode != NULL)
1088 {
1089 size_t r = iconv(cd_utf16, &input, &inbytesleft, &output, &outbytesleft);
1090 if (r != (size_t)-1)
1091 {
1092 output[0] = '\0';
1093 output[1] = '\0';
1094 *unicode = buf_unicode;
1095 }
1096 else
1097 free(buf_unicode);
1098 }
1099 }
1100 #else
1101 if (unicode != NULL)
1102 *unicode = NULL; // no unicode support
1103 #endif
1104 return ret;
1105 #endif
1106 }
1107
Change_directory(const char * path)1108 int Change_directory(const char * path)
1109 {
1110 GFX2_Log(GFX2_DEBUG, "Change_directory(\"%s\")\n", path);
1111 #if defined(__WIN32__) || defined(WIN32)
1112 return (SetCurrentDirectoryA(path) ? 0 : -1);
1113 #else
1114 return chdir(path);
1115 #endif
1116 }
1117
Remove_path(const char * path)1118 int Remove_path(const char * path)
1119 {
1120 #if defined(WIN32)
1121 return (DeleteFileA(path) ? 0 : -1);
1122 #elif defined(__linux__)
1123 return unlink(path);
1124 #else
1125 return remove(path);
1126 #endif
1127 }
1128
1129 ///
1130 /// Remove the directory
Remove_directory(const char * path)1131 int Remove_directory(const char * path)
1132 {
1133 #if defined(WIN32)
1134 return RemoveDirectoryA(path) ? 0 : -1;
1135 #else
1136 return rmdir(path);
1137 #endif
1138 }
1139
1140 ///
1141 /// Calculate relative path
Calculate_relative_path(const char * ref_path,const char * path)1142 char * Calculate_relative_path(const char * ref_path, const char * path)
1143 {
1144 char * real_ref_path;
1145 char * real_path;
1146 char * rel_path = NULL;
1147 int last_separator = -1;
1148 int i;
1149 int separator_count = 0;
1150 size_t len;
1151
1152 if (ref_path == NULL || path == NULL)
1153 return NULL;
1154 real_ref_path = Realpath(ref_path);
1155 if (real_ref_path == NULL)
1156 real_ref_path = strdup(ref_path);
1157 real_path = Realpath(path);
1158 if (real_path == NULL)
1159 real_path = strdup(path);
1160 #if defined(WIN32) || defined(__MINT__)
1161 if (real_ref_path[1] == ':' && real_path[1] == ':')
1162 {
1163 // use same case for drive letter
1164 real_ref_path[0] = (real_ref_path[0] & ~32) | (real_path[0] & 32);
1165 if (real_ref_path[0] != real_path[0])
1166 {
1167 free(real_ref_path);
1168 free(real_path);
1169 return NULL; // path on different volumes, not possible
1170 }
1171 }
1172 #endif
1173 // look for common path parts
1174 for (i = 0; real_ref_path[i] == real_path[i] && real_path[i] != '\0'; i++)
1175 {
1176 if (real_path[i] == PATH_SEPARATOR[0])
1177 last_separator = i;
1178 #if defined(WIN32)
1179 else if(real_path[i] == '/')
1180 last_separator = i;
1181 #endif
1182 }
1183 // at this point, all chars from index 0 to i-1 are identical in
1184 // real_ref_path and path.
1185 // real_ref_path[i] and path[i] are either different, or both '\0'
1186 if (real_ref_path[i] == PATH_SEPARATOR[0] && real_ref_path[i + 1] == '\0' && real_path[i] == '\0')
1187 {
1188 free(real_ref_path);
1189 free(real_path);
1190 return strdup("."); // path are identical (real_ref_path has additional trailing separator)
1191 }
1192 if (real_ref_path[i] == '\0')
1193 {
1194 if (real_path[i] == '\0')
1195 {
1196 free(real_ref_path);
1197 free(real_path);
1198 return strdup("."); // path are identical
1199 }
1200 // path is under ref_path
1201 if (real_path[i] == PATH_SEPARATOR[0])
1202 {
1203 free(real_ref_path);
1204 len = strlen(real_path + i) + 1;
1205 rel_path = GFX2_malloc(len + 1);
1206 if (rel_path != NULL)
1207 snprintf(rel_path, len + 1, ".%s", real_path + i);
1208 free(real_path);
1209 return rel_path;
1210 }
1211 else if (i > 0 && real_ref_path[i - 1] == PATH_SEPARATOR[0])
1212 {
1213 free(real_ref_path);
1214 len = strlen(real_path + i - 1) + 1;
1215 rel_path = GFX2_malloc(len + 1);
1216 if (rel_path != NULL)
1217 snprintf(rel_path, len + 1, ".%s", real_path + i - 1);
1218 free(real_path);
1219 return rel_path;
1220 }
1221 }
1222 if (last_separator <= 0)
1223 {
1224 free(real_ref_path);
1225 return real_path; // no common part found return absolute path
1226 }
1227 // count the number of path separators in the reference path
1228 for (i = last_separator; real_ref_path[i] != '\0'; i++)
1229 {
1230 if (real_ref_path[i] == PATH_SEPARATOR[0] && real_ref_path[i + 1] != '\0') // do not count the trailing separator
1231 separator_count++;
1232 }
1233 free(real_ref_path);
1234 i = 0;
1235 // construct the relative path
1236 len = separator_count * (2 + strlen(PATH_SEPARATOR)) + strlen(real_path + last_separator + 1) + 1;
1237 rel_path = GFX2_malloc(len + 1);
1238 if (rel_path != NULL)
1239 {
1240 while(separator_count-- > 0)
1241 i += snprintf(rel_path + i, len + 1 - i, "..%s", PATH_SEPARATOR);
1242 strncpy(rel_path + i, real_path + last_separator + 1, len + 1 - i);
1243 rel_path[len] = '\0';
1244 }
1245 free(real_path);
1246 return rel_path;
1247 }
1248
1249 #if defined(WIN32)
Enumerate_Network_R(T_Fileselector * list,LPNETRESOURCEA lpnr)1250 static void Enumerate_Network_R(T_Fileselector *list, LPNETRESOURCEA lpnr)
1251 {
1252 // Mpr.lib
1253 HANDLE hEnum;
1254 DWORD r;
1255 r = WNetOpenEnumA (RESOURCE_GLOBALNET, RESOURCETYPE_DISK, 0, lpnr, &hEnum);
1256 if (r == NO_ERROR)
1257 {
1258 DWORD buffer_size = 16*1024;
1259 DWORD count = -1;
1260 LPNETRESOURCEA lpnrLocal = (LPNETRESOURCEA) GlobalAlloc(GPTR, buffer_size);
1261 do
1262 {
1263 ZeroMemory(lpnrLocal, buffer_size);
1264 r = WNetEnumResourceA(hEnum, &count, lpnrLocal, &buffer_size);
1265 if (r == NO_ERROR)
1266 {
1267 DWORD i;
1268 for (i = 0 ; i < count; i++)
1269 {
1270 GFX2_Log(GFX2_DEBUG, "%08x %08x %08x %s %s %s %s\n",
1271 lpnrLocal[i].dwType, lpnrLocal[i].dwDisplayType,
1272 lpnrLocal[i].dwUsage,
1273 lpnrLocal[i].lpProvider, lpnrLocal[i].lpLocalName,
1274 lpnrLocal[i].lpRemoteName, lpnrLocal[i].lpComment);
1275 if (lpnrLocal[i].dwUsage & RESOURCEUSAGE_CONTAINER)
1276 {
1277 Enumerate_Network_R(list, &lpnrLocal[i]);
1278 }
1279 if (lpnrLocal[i].dwType == RESOURCETYPE_DISK &&
1280 lpnrLocal[i].dwDisplayType == RESOURCEDISPLAYTYPE_SHARE)
1281 {
1282 Add_element_to_list(list, lpnrLocal[i].lpRemoteName,
1283 Format_filename(lpnrLocal[i].lpRemoteName, 19-1, FSOBJECT_DRIVE),
1284 FSOBJECT_DRIVE, ICON_NETWORK);
1285 list->Nb_directories++;
1286 }
1287 }
1288 }
1289 else
1290 {
1291 }
1292 }
1293 while (0);
1294 GlobalFree((HGLOBAL) lpnrLocal);
1295 WNetCloseEnum(hEnum);
1296 }
1297 }
1298
Enumerate_Network(T_Fileselector * list)1299 void Enumerate_Network(T_Fileselector *list)
1300 {
1301 Enumerate_Network_R(list, NULL);
1302 }
1303 #endif
1304