naev 0.11.5
nfile.c
Go to the documentation of this file.
1/*
2 * See Licensing and Copyright notice in naev.h
3 */
12#include <dirent.h>
13#include <limits.h>
14#include <stdarg.h>
15#include <stdio.h>
16#include <stdlib.h>
17#include <sys/stat.h>
18#include "physfs.h"
19
20#include "naev.h"
21
22#if HAS_POSIX
23#include <sys/types.h>
24#include <unistd.h>
25#include <errno.h>
26#include <libgen.h>
27#endif /* HAS_POSIX */
28#if __WIN32__
29#include <windows.h>
30#endif /* __WIN32__ */
33#include "nfile.h"
34
35#include "array.h"
36#include "conf.h"
37#if __MACOSX__
38#include "glue_macos.h"
39#endif /* __MACOSX__ */
40#include "log.h"
41#include "nstring.h"
42
43#if HAS_UNIX && !__MACOSX__
45
52static char* xdgGetEnv(const char *name)
53{
54 char *env = SDL_getenv(name);
55 if ((env != NULL) && (env[0] != '\0'))
56 return env;
57 /* What errno signifies missing env var? */
58 errno = EINVAL;
59 return NULL;
60}
61
68static char* xdgEnvDup(const char *name)
69{
70 const char *env;
71 env = xdgGetEnv( name );
72 if (env != NULL)
73 return strdup(env);
74 return NULL;
75}
76
85static char * xdgGetRelativeHome( const char *envname, const char *relativefallback )
86{
87 char *relhome;
88 relhome = xdgEnvDup(envname);
89 if ((relhome == NULL) && (errno != ENOMEM)) {
90 errno = 0;
91 const char *home;
92 unsigned int homelen;
93 home = xdgGetEnv( "HOME" );
94 if (home == NULL)
95 return NULL;
96 homelen = strlen(home);
97 unsigned int fallbacklength;
98 fallbacklength = strlen( relativefallback );
99 relhome = malloc( homelen + fallbacklength + 1 );
100 if (relhome == NULL)
101 return NULL;
102 memcpy( relhome, home, homelen );
103 memcpy( &relhome[ homelen ], relativefallback, fallbacklength + 1 );
104 relhome[ homelen + fallbacklength ] = '\0'; /* Just in case. */
105 }
106 return relhome;
107}
108#endif
109
110static char naev_configPath[PATH_MAX] = "\0";
116const char* nfile_configPath (void)
117{
118 if (naev_configPath[0] == '\0') {
119 /* Global override is set. */
120 if (conf.datapath) {
121 snprintf( naev_configPath, sizeof(naev_configPath), "%s/", conf.datapath );
122 return naev_configPath;
123 }
124#if __MACOSX__
125 if (macos_configPath( naev_configPath, sizeof(naev_configPath) ) != 0) {
126 WARN(_("Cannot determine config path, using current directory."));
127 snprintf( naev_configPath, sizeof(naev_configPath), "./naev/" );
128 }
129#elif HAS_UNIX
130 char *path = xdgGetRelativeHome( "XDG_CONFIG_HOME", "/.config" );
131 if (path == NULL) {
132 WARN(_("$XDG_CONFIG_HOME isn't set, using current directory."));
133 path = strdup(".");
134 }
135
136 snprintf( naev_configPath, sizeof(naev_configPath), "%s/naev/", path );
137 free (path);
138#elif __WIN32__
139 char *path = SDL_getenv("APPDATA");
140 if (path == NULL) {
141 WARN(_("%%APPDATA%% isn't set, using current directory."));
142 path = ".";
143 }
144 snprintf( naev_configPath, sizeof(naev_configPath), "%s/naev/", path );
145#else
146#error "Feature needs implementation on this Operating System for Naev to work."
147#endif
148 }
149
150 return naev_configPath;
151}
152
153static char naev_cachePath[PATH_MAX] = "\0";
159const char* nfile_cachePath (void)
160{
161 if (naev_cachePath[0] == '\0') {
162 /* Global override is set. */
163 if (conf.datapath) {
164 snprintf( naev_cachePath, sizeof(naev_cachePath), "%s/", conf.datapath );
165 return naev_cachePath;
166 }
167#if __MACOSX__
168 if (macos_cachePath( naev_cachePath, sizeof(naev_cachePath) ) != 0) {
169 WARN(_("Cannot determine cache path, using current directory."));
170 snprintf( naev_cachePath, sizeof(naev_cachePath), "./naev/" );
171 }
172#elif HAS_UNIX
173 char *path = xdgGetRelativeHome( "XDG_CACHE_HOME", "/.cache" );
174 if (path == NULL) {
175 WARN(_("$XDG_CACHE_HOME isn't set, using current directory."));
176 path = strdup(".");
177 }
178
179 snprintf( naev_cachePath, sizeof(naev_cachePath), "%s/naev/", path );
180 free (path);
181#elif __WIN32__
182 char *path = SDL_getenv("APPDATA");
183 if (path == NULL) {
184 WARN(_("%%APPDATA%% isn't set, using current directory."));
185 path = ".";
186 }
187 snprintf( naev_cachePath, sizeof(naev_cachePath), "%s/naev/", path );
188#else
189#error "Feature needs implementation on this Operating System for Naev to work."
190#endif
191 }
192
193 return naev_cachePath;
194}
195
196#if HAS_POSIX
197#define MKDIR mkdir( opath, mode )
198static int mkpath( const char *path, mode_t mode )
199#elif __WIN32__
200#define MKDIR !CreateDirectory( opath, NULL )
201static int mkpath( const char *path )
202#else
203#error "Feature needs implementation on this Operating System for Naev to work."
204#endif
205{
206 char opath[PATH_MAX];
207 char *p;
208 size_t len;
209 int ret;
210
211 if (path == NULL)
212 return 0;
213
214 strncpy( opath, path, sizeof(opath)-1 );
215 opath[ sizeof(opath)-1 ] = '\0';
216 len = strlen(opath);
217
218 p = &opath[len-1];
219 if (nfile_isSeparator(p[0])) {
220 p[0] = '\0';
221 p--;
222 }
223
224 // Traverse up until we find a directory that exists.
225 for (; p >= opath; p--) {
226 if (nfile_isSeparator(p[0])) {
227 p[0] = '\0';
228 if (nfile_dirExists(opath)) {
229 p[0] = '/';
230 break;
231 }
232 p[0] = '/';
233 }
234 }
235 // This skips the directory that exists, or puts us
236 // back at the start if the loop fell through.
237 p++;
238
239 // Traverse down, creating directories.
240 for (; p[0] != '\0'; p++) {
241 if (nfile_isSeparator(p[0])) {
242 p[0] = '\0';
243 ret = MKDIR;
244 if (ret)
245 return ret;
246 p[0] = '/';
247 }
248 }
249
250 // Create the final directory.
251 if (!nfile_dirExists(opath)) {
252 ret = MKDIR;
253 if (ret)
254 return ret;
255 }
256
257 return 0;
258}
259#undef MKDIR
260
267int nfile_dirMakeExist( const char *path )
268{
269 if ( path == NULL )
270 return -1;
271
272 /* Check if it exists. */
273 if ( nfile_dirExists( path ) )
274 return 0;
275
276#if HAS_POSIX
277 if ( mkpath( path, S_IRWXU | S_IRWXG | S_IRWXO ) < 0 ) {
278#elif __WIN32__
279 if ( mkpath( path ) < 0 ) {
280#else
281#error "Feature needs implementation on this Operating System for Naev to work."
282#endif
283 WARN( _( "Dir '%s' does not exist and unable to create: %s" ), path, strerror( errno ) );
284 return -1;
285 }
286
287 return 0;
288}
289
296int nfile_dirExists( const char *path )
297{
298 DIR *d;
299
300 if (path == NULL)
301 return -1;
302
303 d = opendir( path );
304 if ( d == NULL )
305 return 0;
306 closedir(d);
307 return 1;
308}
309
316int nfile_fileExists( const char *path )
317{
318 struct stat buf;
319
320 if ( path == NULL )
321 return -1;
322
323 if ( stat( path, &buf ) == 0 ) /* stat worked, file must exist */
324 return 1;
325
326 /* ANSI C89 compliant method here for reference. Not as precise as stat.
327 FILE *f = fopen(file, "rb");
328 if (f != NULL) {
329 fclose(f);
330 return 1;
331 }
332 */
333
334 return 0;
335}
336
343int nfile_backupIfExists( const char *path )
344{
345 char backup[ PATH_MAX ];
346
347 if ( path == NULL )
348 return -1;
349
350 if ( !nfile_fileExists( path ) )
351 return 0;
352
353 snprintf(backup, sizeof(backup), "%s.backup", path);
354
355 return nfile_copyIfExists( path, backup );
356}
357
365int nfile_copyIfExists( const char* file1, const char* file2 )
366{
367 FILE *f_in, *f_out;
368 char buf[ 8*1024 ];
369 size_t lr, lw;
370
371 if (file1 == NULL)
372 return -1;
373
374 /* Check if input file exists */
375 if (!nfile_fileExists(file1))
376 return 0;
377
378 /* Open files. */
379 f_in = fopen( file1, "rb" );
380 f_out = fopen( file2, "wb" );
381 if ((f_in==NULL) || (f_out==NULL)) {
382 WARN( _("Failure to copy '%s' to '%s': %s"), file1, file2, strerror(errno) );
383 if (f_in!=NULL)
384 fclose(f_in);
385 if (f_out!=NULL)
386 fclose(f_out);
387 return -1;
388 }
389
390 /* Copy data over. */
391 do {
392 lr = fread( buf, 1, sizeof(buf), f_in );
393 if (ferror(f_in))
394 goto err;
395 else if (!lr) {
396 if (feof(f_in))
397 break;
398 goto err;
399 }
400
401 lw = fwrite( buf, 1, lr, f_out );
402 if (ferror(f_out) || (lr != lw))
403 goto err;
404 } while (lr > 0);
405
406 /* Close files. */
407 fclose( f_in );
408 fclose( f_out );
409
410 return 0;
411
412err:
413 WARN( _("Failure to copy '%s' to '%s': %s"), file1, file2, strerror(errno) );
414 fclose( f_in );
415 fclose( f_out );
416
417 return -1;
418}
419
427char *nfile_readFile( size_t *filesize, const char *path )
428{
429 int n;
430 char *buf;
431 FILE *file;
432 int len;
433 size_t pos;
434 struct stat path_stat;
435
436 if ( path == NULL ) {
437 *filesize = 0;
438 return NULL;
439 }
440
441 if ( stat( path, &path_stat ) ) {
442 WARN( _( "Error occurred while opening '%s': %s" ), path, strerror( errno ) );
443 *filesize = 0;
444 return NULL;
445 }
446
447 if ( !S_ISREG( path_stat.st_mode ) ) {
448 WARN( _( "Error occurred while opening '%s': It is not a regular file" ), path );
449 *filesize = 0;
450 return NULL;
451 }
452
453 /* Open file. */
454 file = fopen( path, "rb" );
455 if ( file == NULL ) {
456 WARN( _( "Error occurred while opening '%s': %s" ), path, strerror( errno ) );
457 *filesize = 0;
458 return NULL;
459 }
460
461 /* Get file size. */
462 if ( fseek( file, 0L, SEEK_END ) == -1 ) {
463 WARN( _( "Error occurred while seeking '%s': %s" ), path, strerror( errno ) );
464 fclose( file );
465 *filesize = 0;
466 return NULL;
467 }
468 len = ftell( file );
469 if ( fseek( file, 0L, SEEK_SET ) == -1 ) {
470 WARN( _( "Error occurred while seeking '%s': %s" ), path, strerror( errno ) );
471 fclose( file );
472 *filesize = 0;
473 return NULL;
474 }
475
476 /* Allocate buffer. */
477 buf = malloc( len+1 );
478 if (buf == NULL) {
479 WARN(_("Out of Memory"));
480 fclose(file);
481 *filesize = 0;
482 return NULL;
483 }
484 buf[len] = '\0';
485
486 /* Read the file. */
487 n = 0;
488 while ( n < len ) {
489 pos = fread( &buf[ n ], 1, len - n, file );
490 if ( pos <= 0 ) {
491 WARN( _( "Error occurred while reading '%s': %s" ), path, strerror( errno ) );
492 fclose( file );
493 *filesize = 0;
494 free(buf);
495 return NULL;
496 }
497 n += pos;
498 }
499
500 /* Close the file. */
501 fclose(file);
502
503 *filesize = len;
504 return buf;
505}
506
512int nfile_touch( const char *path )
513{
514 FILE *f;
515
516 if (path == NULL)
517 return -1;
518
519 /* Try to open the file, C89 compliant, but not as precise as stat. */
520 f = fopen( path, "a+b" );
521 if ( f == NULL ) {
522 WARN( _( "Unable to touch file '%s': %s" ), path, strerror( errno ) );
523 return -1;
524 }
525
526 fclose(f);
527 return 0;
528}
529
538int nfile_writeFile( const char *data, size_t len, const char *path )
539{
540 size_t n;
541 FILE *file;
542 size_t pos;
543
544 if ( path == NULL )
545 return -1;
546
547 /* Open file. */
548 file = fopen( path, "wb" );
549 if ( file == NULL ) {
550 WARN( _( "Error occurred while opening '%s': %s" ), path, strerror( errno ) );
551 return -1;
552 }
553
554 /* Write the file. */
555 n = 0;
556 while ( n < len ) {
557 pos = fwrite( &data[ n ], 1, len - n, file );
558 if ( pos <= 0 ) {
559 WARN( _( "Error occurred while writing '%s': %s" ), path, strerror( errno ) );
560 fclose( file ); /* don't care about further errors */
561 return -1;
562 }
563 n += pos;
564 }
565
566 /* Close the file. */
567 if ( fclose( file ) == EOF ) {
568 WARN( _( "Error occurred while closing '%s': %s" ), path, strerror( errno ) );
569 return -1;
570 }
571
572 return 0;
573}
574
581int nfile_isSeparator( uint32_t c )
582{
583 if (c == '/')
584 return 1;
585#if __WIN32__
586 else if (c == '\\')
587 return 1;
588#endif /* __WIN32__ */
589 return 0;
590}
591
592int _nfile_concatPaths( char buf[static 1], int maxLength, const char path[static 1], ... )
593{
594 char *bufPos;
595 char *bufEnd;
596 const char *section;
597 va_list ap;
598
599 bufPos = buf;
600 bufEnd = buf + maxLength;
601 va_start( ap, path );
602 section = path;
603
604#if DEBUGGING
605 if ( section == NULL )
606 WARN( _( "First argument to nfile_concatPaths was NULL. This is probably an error." ) );
607#endif
608
609 do {
610 // End of arg list?
611 if ( section == NULL )
612 break;
613
614 if ( bufPos > buf ) {
615 // Make sure there's a path seperator.
616 if ( bufPos[ -1 ] != '/' ) {
617 bufPos[ 0 ] = '/';
618 bufPos += 1;
619 }
620 // But not too many path seperators.
621 if ( *section == '/' )
622 section += 1;
623 }
624
625 // Copy this section
626 bufPos = memccpy( bufPos, section, '\0', bufEnd - bufPos );
627 if ( bufPos == NULL )
628 break;
629
630 // Next path section
631 section = va_arg( ap, char * );
632 } while ( bufPos-- < bufEnd ); // Rewind after compare so we're pointing at the NULL character.
633 va_end( ap );
634
635 // Did we run out of space?
636 if ( section != NULL )
637 return -1;
638
639 return bufPos - buf;
640}
Provides macros to work with dynamic arrays.
Header file with generic functions and naev-specifics.
#define PATH_MAX
Definition naev.h:50
int nfile_copyIfExists(const char *file1, const char *file2)
Copy a file, if it exists.
Definition nfile.c:365
const char * nfile_configPath(void)
Gets Naev's config path (for user preferences such as conf.lua)
Definition nfile.c:116
static char naev_configPath[PATH_MAX]
Definition nfile.c:110
int nfile_writeFile(const char *data, size_t len, const char *path)
Tries to write a file.
Definition nfile.c:538
static char naev_cachePath[PATH_MAX]
Definition nfile.c:153
char * nfile_readFile(size_t *filesize, const char *path)
Tries to read a file.
Definition nfile.c:427
int nfile_dirMakeExist(const char *path)
Creates a directory if it doesn't exist.
Definition nfile.c:267
int nfile_backupIfExists(const char *path)
Backup a file, if it exists.
Definition nfile.c:343
const char * nfile_cachePath(void)
Gets Naev's cache path (for cached data such as generated textures)
Definition nfile.c:159
int nfile_fileExists(const char *path)
Checks to see if a file exists.
Definition nfile.c:316
int nfile_touch(const char *path)
Tries to create the file if it doesn't exist.
Definition nfile.c:512
int nfile_isSeparator(uint32_t c)
Checks to see if a character is used to separate files in a path.
Definition nfile.c:581
int nfile_dirExists(const char *path)
Checks to see if a directory exists.
Definition nfile.c:296
static const double c[]
Definition rng.c:264
static const double d[]
Definition rng.c:273
char * datapath
Definition conf.h:76