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