Ticket #36: io.c

File io.c, 15.1 KB (added by paul.wratt, 8 years ago)

Adds For_each_font_in_tree() (needs to be declared in io.h too), calls Add_font() in text.c

Line 
1/* vim:expandtab:ts=2 sw=2:
2*/
3/* Grafx2 - The Ultimate 256-color bitmap paint program
4
5 Copyright 2011 Pawel Góralski
6 Copyright 2008 Yves Rizoud
7 Copyright 2007 Adrien Destugues
8 Copyright 1996-2001 Sunset Design (Guillaume Dorme & Karl Maritaud)
9
10 Grafx2 is free software; you can redistribute it and/or
11 modify it under the terms of the GNU General Public License
12 as published by the Free Software Foundation; version 2
13 of the License.
14
15 Grafx2 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 General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with Grafx2; if not, see <http://www.gnu.org/licenses/>
22*/
23
24// Fonctions de lecture/ecriture file, gèrent les systèmes big-endian et
25// little-endian.
26
27#define _XOPEN_SOURCE 500
28
29#include <SDL_endian.h>
30#include <string.h>
31#include <sys/stat.h>
32#include <errno.h>
33#include <fcntl.h>
34#include <unistd.h>
35
36#if defined(__amigaos4__) || defined(__AROS__) || defined(__MORPHOS__) || defined(__amigaos__)
37 #include <proto/dos.h>
38 #include <sys/types.h>
39 #include <dirent.h>
40#elif defined(__WIN32__)
41 #include <dirent.h>
42 #include <windows.h>
43 //#include <commdlg.h>
44#elif defined(__MINT__)
45 #include <mint/osbind.h>
46 #include <mint/sysbind.h>
47 #include <dirent.h>
48#else
49 #include <dirent.h>
50#endif
51
52#include "struct.h"
53#include "io.h"
54#include "realpath.h"
55
56#include "text.h"
57
58// Lit un octet
59// Renvoie -1 si OK, 0 en cas d'erreur
60int Read_byte(FILE *file, byte *dest)
61{
62 return fread(dest, 1, 1, file) == 1;
63}
64// Ecrit un octet
65// Renvoie -1 si OK, 0 en cas d'erreur
66int Write_byte(FILE *file, byte b)
67{
68 return fwrite(&b, 1, 1, file) == 1;
69}
70// Lit des octets
71// Renvoie -1 si OK, 0 en cas d'erreur
72int Read_bytes(FILE *file, void *dest, size_t size)
73{
74 return fread(dest, 1, size, file) == size;
75}
76// Ecrit des octets
77// Renvoie -1 si OK, 0 en cas d'erreur
78int Write_bytes(FILE *file, void *src, size_t size)
79{
80 return fwrite(src, 1, size, file) == size;
81}
82
83// Lit un word (little-endian)
84// Renvoie -1 si OK, 0 en cas d'erreur
85int Read_word_le(FILE *file, word *dest)
86{
87 if (fread(dest, 1, sizeof(word), file) != sizeof(word))
88 return 0;
89 #if SDL_BYTEORDER != SDL_LIL_ENDIAN
90 *dest = SDL_Swap16(*dest);
91 #endif
92 return -1;
93}
94// Ecrit un word (little-endian)
95// Renvoie -1 si OK, 0 en cas d'erreur
96int Write_word_le(FILE *file, word w)
97{
98 #if SDL_BYTEORDER != SDL_LIL_ENDIAN
99 w = SDL_Swap16(w);
100 #endif
101 return fwrite(&w, 1, sizeof(word), file) == sizeof(word);
102}
103// Lit un word (big-endian)
104// Renvoie -1 si OK, 0 en cas d'erreur
105int Read_word_be(FILE *file, word *dest)
106{
107 if (fread(dest, 1, sizeof(word), file) != sizeof(word))
108 return 0;
109 #if SDL_BYTEORDER != SDL_BIG_ENDIAN
110 *dest = SDL_Swap16(*dest);
111 #endif
112 return -1;
113}
114// Ecrit un word (big-endian)
115// Renvoie -1 si OK, 0 en cas d'erreur
116int Write_word_be(FILE *file, word w)
117{
118 #if SDL_BYTEORDER != SDL_BIG_ENDIAN
119 w = SDL_Swap16(w);
120 #endif
121 return fwrite(&w, 1, sizeof(word), file) == sizeof(word);
122}
123// Lit un dword (little-endian)
124// Renvoie -1 si OK, 0 en cas d'erreur
125int Read_dword_le(FILE *file, dword *dest)
126{
127 if (fread(dest, 1, sizeof(dword), file) != sizeof(dword))
128 return 0;
129 #if SDL_BYTEORDER != SDL_LIL_ENDIAN
130 *dest = SDL_Swap32(*dest);
131 #endif
132 return -1;
133}
134// Ecrit un dword (little-endian)
135// Renvoie -1 si OK, 0 en cas d'erreur
136int Write_dword_le(FILE *file, dword dw)
137{
138 #if SDL_BYTEORDER != SDL_LIL_ENDIAN
139 dw = SDL_Swap32(dw);
140 #endif
141 return fwrite(&dw, 1, sizeof(dword), file) == sizeof(dword);
142}
143
144// Lit un dword (big-endian)
145// Renvoie -1 si OK, 0 en cas d'erreur
146int Read_dword_be(FILE *file, dword *dest)
147{
148 if (fread(dest, 1, sizeof(dword), file) != sizeof(dword))
149 return 0;
150 #if SDL_BYTEORDER != SDL_BIG_ENDIAN
151 *dest = SDL_Swap32(*dest);
152 #endif
153 return -1;
154}
155// Ecrit un dword (big-endian)
156// Renvoie -1 si OK, 0 en cas d'erreur
157int Write_dword_be(FILE *file, dword dw)
158{
159 #if SDL_BYTEORDER != SDL_BIG_ENDIAN
160 dw = SDL_Swap32(dw);
161 #endif
162 return fwrite(&dw, 1, sizeof(dword), file) == sizeof(dword);
163}
164
165// Détermine la position du dernier '/' ou '\\' dans une chaine,
166// typiquement pour séparer le nom de file d'un chemin.
167// Attention, sous Windows, il faut s'attendre aux deux car
168// par exemple un programme lancé sous GDB aura comme argv[0]:
169// d:\Data\C\GFX2\grafx2/grafx2.exe
170char * Find_last_separator(const char * str)
171{
172 const char * position = NULL;
173 for (; *str != '\0'; str++)
174 if (*str == PATH_SEPARATOR[0]
175#ifdef __WIN32__
176 || *str == '/'
177#elif __AROS__
178 || *str == ':'
179#endif
180 )
181 position = str;
182 return (char *)position;
183}
184// Récupère la partie "nom de file seul" d'un chemin
185void Extract_filename(char *dest, const char *source)
186{
187 const char * position = Find_last_separator(source);
188
189 if (position)
190 strcpy(dest,position+1);
191 else
192 strcpy(dest,source);
193}
194// Récupère la partie "répertoire+/" d'un chemin.
195void Extract_path(char *dest, const char *source)
196{
197 char * position=NULL;
198
199 Realpath(source,dest);
200 position = Find_last_separator(dest);
201 if (position)
202 *(position+1) = '\0';
203 else
204 strcat(dest, PATH_SEPARATOR);
205}
206
207///
208/// Appends a file or directory name to an existing directory name.
209/// As a special case, when the new item is equal to PARENT_DIR, this
210/// will remove the rightmost directory name.
211/// reverse_path is optional, if it's non-null, the function will
212/// write there :
213/// - if filename is ".." : The name of eliminated directory/file
214/// - else: ".."
215void Append_path(char *path, const char *filename, char *reverse_path)
216{
217 // Parent
218 if (!strcmp(filename, PARENT_DIR))
219 {
220 // Going up one directory
221 long len;
222 char * separator_pos;
223
224 // Remove trailing slash
225 len=strlen(path);
226 if (len && (!strcmp(path+len-1,PATH_SEPARATOR)
227 #ifdef __WIN32__
228 || path[len-1]=='/'
229 #endif
230 ))
231 path[len-1]='\0';
232
233 separator_pos=Find_last_separator(path);
234 if (separator_pos)
235 {
236 if (reverse_path)
237 strcpy(reverse_path, separator_pos+1);
238 #if defined(__AROS__)
239 // Don't strip away the colon
240 if (*separator_pos == ':') *(separator_pos+1)='\0';
241 else *separator_pos='\0';
242 #else
243 *separator_pos='\0';
244 #endif
245 }
246 else
247 {
248 if (reverse_path)
249 strcpy(reverse_path, path);
250 path[0]='\0';
251 }
252 #if defined(__WIN32__)
253 // Roots of drives need a pending antislash
254 if (path[0]!='\0' && path[1]==':' && path[2]=='\0')
255 {
256 strcat(path, PATH_SEPARATOR);
257 }
258 #endif
259 }
260 else
261 // Sub-directory
262 {
263 long len;
264 // Add trailing slash if needed
265 len=strlen(path);
266 if (len && (strcmp(path+len-1,PATH_SEPARATOR)
267 #ifdef __WIN32__
268 && path[len-1]!='/'
269 #elif __AROS__
270 && path[len-1]!=':' // To avoid paths like volume:/dir
271 #endif
272 ))
273 {
274 strcpy(path+len, PATH_SEPARATOR);
275 len+=strlen(PATH_SEPARATOR);
276 }
277 strcat(path, filename);
278
279 if (reverse_path)
280 strcpy(reverse_path, PARENT_DIR);
281 }
282}
283
284int File_exists(char * fname)
285// Détermine si un file passé en paramètre existe ou non dans le
286// répertoire courant.
287{
288 struct stat buf;
289 int result;
290
291 result=stat(fname,&buf);
292 if (result!=0)
293 return(errno!=ENOENT);
294 else
295 return 1;
296
297}
298int Directory_exists(char * directory)
299// Détermine si un répertoire passé en paramètre existe ou non dans le
300// répertoire courant.
301{
302 DIR* entry; // Structure de lecture des éléments
303
304 if (strcmp(directory,PARENT_DIR)==0)
305 return 1;
306 else
307 {
308 // On va chercher si le répertoire existe à l'aide d'un Opendir. S'il
309 // renvoie NULL c'est que le répertoire n'est pas accessible...
310
311 entry=opendir(directory);
312 if (entry==NULL)
313 return 0;
314 else
315 {
316 closedir(entry);
317 return 1;
318 }
319 }
320}
321
322/// Check if a file or directory is hidden.
323int File_is_hidden(const char *fname, const char *full_name)
324{
325#if defined(__amigaos4__) || defined(__AROS__) || defined(__MORPHOS__) || defined(__amigaos__) || defined(__MINT__)
326 // False (unable to determine, or irrelevent for platform)
327 (void)fname;//unused
328 (void)full_name;//unused
329 return 0;
330#elif defined(__WIN32__)
331 unsigned long att;
332 if (full_name!=NULL)
333 att = GetFileAttributesA(full_name);
334 else
335 att = GetFileAttributesA(fname);
336 if (att==INVALID_FILE_ATTRIBUTES)
337 return 0;
338 return (att&FILE_ATTRIBUTE_HIDDEN)?1:0;
339#else
340 (void)full_name;//unused
341 // On linux/unix (default), files are considered hidden if their name
342 // begins with a .
343 // As a special case, we'll consider 'parent directory' (..) never hidden.
344 return fname[0]=='.' && strcmp(fname, PARENT_DIR);
345#endif
346}
347// Taille de fichier, en octets
348int File_length(const char * fname)
349{
350 struct stat infos_fichier;
351 if (stat(fname,&infos_fichier))
352 return 0;
353 return infos_fichier.st_size;
354}
355int File_length_file(FILE * file)
356{
357 struct stat infos_fichier;
358 if (fstat(fileno(file),&infos_fichier))
359 return 0;
360 return infos_fichier.st_size;
361}
362
363void For_each_file(const char * directory_name, void Callback(const char *))
364{
365 // Pour scan de répertoire
366 DIR* current_directory; //Répertoire courant
367 struct dirent* entry; // Structure de lecture des éléments
368 char full_filename[MAX_PATH_CHARACTERS];
369 int filename_position;
370 strcpy(full_filename, directory_name);
371 current_directory=opendir(directory_name);
372 if(current_directory == NULL) return; // Répertoire invalide ...
373 filename_position = strlen(full_filename);
374#if defined(__AROS__)
375 if (filename_position==0 || (strcmp(full_filename+filename_position-1,PATH_SEPARATOR) && strcmp(full_filename+filename_position-1,":")))
376#else
377 if (filename_position==0 || strcmp(full_filename+filename_position-1,PATH_SEPARATOR))
378#endif
379 {
380 strcat(full_filename, PATH_SEPARATOR);
381 filename_position = strlen(full_filename);
382 }
383 while ((entry=readdir(current_directory)))
384 {
385 struct stat Infos_enreg;
386 strcpy(&full_filename[filename_position], entry->d_name);
387 stat(full_filename,&Infos_enreg);
388 if (S_ISREG(Infos_enreg.st_mode))
389 {
390 Callback(full_filename);
391 }
392 }
393 closedir(current_directory);
394}
395
396void For_each_font_in_tree(const char * directory_name)
397{
398 // Pour scan de répertoire
399 DIR* current_directory; //Répertoire courant
400 struct dirent* entry; // Structure de lecture des éléments
401 char full_filename[MAX_PATH_CHARACTERS];
402 int filename_position;
403 strcpy(full_filename, directory_name);
404 current_directory=opendir(directory_name);
405 if(current_directory == NULL) return; // Répertoire invalide ...
406 filename_position = strlen(full_filename);
407#if defined(__AROS__)
408 if (filename_position==0 || (strcmp(full_filename+filename_position-1,PATH_SEPARATOR) && strcmp(full_filename+filename_position-1,":")))
409#else
410 if (filename_position==0 || strcmp(full_filename+filename_position-1,PATH_SEPARATOR))
411#endif
412 {
413 strcat(full_filename, PATH_SEPARATOR);
414 filename_position = strlen(full_filename);
415 }
416 while ((entry=readdir(current_directory)))
417 {
418 struct stat Infos_enreg;
419 strcpy(&full_filename[filename_position], entry->d_name);
420 stat(full_filename,&Infos_enreg);
421 if (S_ISREG(Infos_enreg.st_mode))
422 {
423 Add_font(full_filename);
424 }else if (S_ISDIR(Infos_enreg.st_mode))
425 {
426 if (strcmp(entry->d_name,".") != 0 && strcmp(entry->d_name,"..") != 0)
427 For_each_font_in_tree(full_filename);
428 }
429 }
430 closedir(current_directory);
431}
432
433/// Scans a directory, calls Callback for each file or directory in it,
434void For_each_directory_entry(const char * directory_name, void Callback(const char *, byte is_file, byte is_directory, byte is_hidden))
435{
436 // Pour scan de répertoire
437 DIR* current_directory; //Répertoire courant
438 struct dirent* entry; // Structure de lecture des éléments
439 char full_filename[MAX_PATH_CHARACTERS];
440 int filename_position;
441 strcpy(full_filename, directory_name);
442 current_directory=opendir(full_filename);
443 if(current_directory == NULL) return; // Répertoire invalide ...
444 filename_position = strlen(full_filename);
445#if defined(__AROS__)
446 if (filename_position==0 || (strcmp(full_filename+filename_position-1,PATH_SEPARATOR) && strcmp(full_filename+filename_position-1,":")))
447#else
448 if (filename_position==0 || strcmp(full_filename+filename_position-1,PATH_SEPARATOR))
449#endif
450 {
451 strcat(full_filename, PATH_SEPARATOR);
452 filename_position = strlen(full_filename);
453 }
454 while ((entry=readdir(current_directory)))
455 {
456 struct stat Infos_enreg;
457 strcpy(&full_filename[filename_position], entry->d_name);
458 stat(full_filename,&Infos_enreg);
459 Callback(
460 full_filename,
461 S_ISREG(Infos_enreg.st_mode),
462 S_ISDIR(Infos_enreg.st_mode),
463 File_is_hidden(entry->d_name, full_filename));
464 }
465 closedir(current_directory);
466}
467
468
469void Get_full_filename(char * output_name, char * file_name, char * directory_name)
470{
471 strcpy(output_name,directory_name);
472 if (output_name[0] != '\0')
473 {
474 // Append a separator at the end of path, if there isn't one already.
475 // This handles the case of directory variables which contain one,
476 // as well as directories like "/" on Unix.
477#if defined(__AROS__)
478 // additional check for ':' to avoid paths like PROGDIR:/unnamed.gif
479 if ((output_name[strlen(output_name)-1]!=PATH_SEPARATOR[0]) && (output_name[strlen(output_name)-1]!=':'))
480#else
481 if (output_name[strlen(output_name)-1]!=PATH_SEPARATOR[0])
482#endif
483 strcat(output_name,PATH_SEPARATOR);
484 }
485 strcat(output_name,file_name);
486}
487
488/// Lock file used to prevent several instances of grafx2 from harming each others' backups
489#ifdef __WIN32__
490HANDLE Lock_file_handle = INVALID_HANDLE_VALUE;
491#else
492int Lock_file_handle = -1;
493#endif
494
495byte Create_lock_file(const char *file_directory)
496{
497 #if defined (__amigaos__)||(__AROS__)
498 #warning "Missing code for your platform, please check and correct!"
499 #else
500 char lock_filename[MAX_PATH_CHARACTERS];
501
502 strcpy(lock_filename,file_directory);
503 strcat(lock_filename,"gfx2.lck");
504
505 #ifdef __WIN32__
506 // Windowzy method for creating a lock file
507 Lock_file_handle = CreateFile(
508 lock_filename,
509 GENERIC_WRITE,
510 0, // No sharing
511 NULL,
512 OPEN_ALWAYS,
513 FILE_ATTRIBUTE_NORMAL,
514 NULL);
515 if (Lock_file_handle == INVALID_HANDLE_VALUE)
516 {
517 return -1;
518 }
519 #else
520 // Unixy method for lock file
521 Lock_file_handle = open(lock_filename,O_WRONLY|O_CREAT,S_IRUSR|S_IWUSR);
522 if (Lock_file_handle == -1)
523 {
524 // Usually write-protected media
525 return -1;
526 }
527 if (lockf(Lock_file_handle, F_TLOCK, 0)==-1)
528 {
529 close(Lock_file_handle);
530 // Usually write-protected media
531 return -1;
532 }
533 #endif
534 #endif // __amigaos__ or __AROS__
535 return 0;
536}
537
538void Release_lock_file(const char *file_directory)
539{
540 char lock_filename[MAX_PATH_CHARACTERS];
541
542 #ifdef __WIN32__
543 if (Lock_file_handle != INVALID_HANDLE_VALUE)
544 {
545 CloseHandle(Lock_file_handle);
546 }
547 #else
548 if (Lock_file_handle != -1)
549 {
550 close(Lock_file_handle);
551 Lock_file_handle = -1;
552 }
553 #endif
554
555 // Actual deletion
556 strcpy(lock_filename,file_directory);
557 strcat(lock_filename,"gfx2.lck");
558 remove(lock_filename);
559}