1 /************************************************************************
3 * voxelands - 3d voxel world sandbox game
4 * Copyright (C) Lisa 'darkrose' Milne 2016 <lisa@ltmnet.com>
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14 * See the GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>
18 ************************************************************************/
29 #include <sys/types.h>
43 char* cwd
; /* current working directory */
44 char* data_custom
; /* set by config data_path */
45 char* data_user
; /* ~/.local/share/voxelands */
46 char* data_global
; /* /usr/share/voxelands */
47 char* data
; /* ./data if it exists */
48 char* game
; /* data_user + /worlds/ + world name */
50 char* config
; /* ~/.config/voxelands */
51 char* screenshot
; /* set by config screenshot_path */
64 static int path_check(char* base
, char* rel
)
76 l
= snprintf(path
,2048,"%s",base
);
81 l
= snprintf(path
,2048,"%s/%s",base
,rel
);
83 l
= snprintf(path
,2048,"%s",rel
);
89 if (stat(path
,&st
) != 0)
91 if ((st
.st_mode
&S_IFMT
) == S_IFDIR
)
93 if ((st
.st_mode
&S_IFMT
) == S_IFREG
)
96 atts
= GetFileAttributes(path
);
97 if (atts
== INVALID_FILE_ATTRIBUTES
)
99 if (atts
== FILE_ATTRIBUTE_DIRECTORY
)
101 if (atts
== FILE_ATTRIBUTE_NORMAL
)
108 static char* path_set(char* base
, char* rel
, char* buff
, int size
)
114 l
= snprintf(path
,2048,"%s/%s",base
,rel
);
116 l
= snprintf(path
,2048,"%s",rel
);
127 if (!strcpy(buff
,path
))
133 static int dir_create(char* p
);
134 static int dir_create(char* path
)
137 if (CreateDirectory(path
, NULL
))
139 if (GetLastError() == ERROR_ALREADY_EXISTS
)
142 mode_t process_mask
= umask(0);
143 mode_t mode
= S_IRWXU
| S_IRWXG
| S_IRWXO
;
149 if (!strcmp(path
, ".") || !strcmp(path
, "/")) {
154 if ((p
= strdup(path
)) == NULL
) {
159 if ((q
= strdup(path
)) == NULL
) {
164 if ((r
= dirname(q
)) == NULL
)
167 if ((up
= strdup(r
)) == NULL
) {
172 if ((dir_create(up
) == 1) && (errno
!= EEXIST
))
175 if ((mkdir(p
, mode
) == -1) && (errno
!= EEXIST
)) {
193 int unlink_cb(const char* fpath
, const struct stat
*sb
, int typeflag
, struct FTW
*ftwbuf
)
195 return remove(fpath
);
199 /* initialises the common paths */
204 path
.data_global
= strdup(GAMEDATA
);
207 if (getcwd(buff
,2048)) {
208 path
.cwd
= strdup(buff
);
210 path
.cwd
= strdup(".");
213 path
.home
= getenv("HOME");
215 path
.home
= strdup(path
.home
);
217 path
.home
= strdup(path
.cwd
);
220 path
.data_user
= getenv("XDG_DATA_HOME");
221 if (path
.data_user
) {
222 path
.data_user
= path_set(path
.data_user
,"voxelands",NULL
,0);
223 }else if (path
.home
) {
224 path
.data_user
= path_set(path
.home
,".local/share/voxelands",NULL
,0);
226 path
.data_user
= path_set(path
.cwd
,"data",NULL
,0);
229 path
.config
= getenv("XDG_CONFIG_HOME");
231 path
.config
= path_set(path
.config
,"voxelands",NULL
,0);
232 }else if (path
.home
) {
233 path
.config
= path_set(path
.home
,".config/voxelands",NULL
,0);
235 path
.config
= strdup(path
.cwd
);
238 /* TODO: windows, and mac? */
241 if (snprintf(buff
,2048,"%s/data",path
.cwd
) < 2048 && path_check(NULL
,buff
) == 2)
242 path
.data
= strdup(buff
);
247 /* frees all the paths */
254 if (path
.data_custom
)
255 free(path
.data_custom
);
256 path
.data_custom
= NULL
;
259 free(path
.data_user
);
260 path
.data_user
= NULL
;
262 if (path
.data_global
)
263 free(path
.data_global
);
264 path
.data_global
= NULL
;
283 free(path
.screenshot
);
284 path
.screenshot
= NULL
;
287 /* sets path.data_custom */
288 int path_custom_setter(char* p
)
290 if (path
.data_custom
)
291 free(path
.data_custom
);
292 path
.data_custom
= NULL
;
295 path
.data_custom
= strdup(p
);
300 /* sets path.screenshot */
301 int path_screenshot_setter(char* p
)
304 free(path
.screenshot
);
305 path
.screenshot
= NULL
;
308 path
.screenshot
= strdup(p
);
313 /* sets the game/world path to user_data + /worlds/ + p, creates the path if necessary */
314 int path_game_setter(char* p
)
318 char* base
= path
.data_user
;
327 if (snprintf(buff
,2048,"worlds/%s",p
) >= 2048)
339 path
.game
= path_set(base
,buff
,NULL
,0);
341 c
= path_check(path
.game
,NULL
);
345 return dir_create(path
.game
);
350 * get the full path for a file/directory
351 * type is the usual "texture" "model" etc
352 * file is the file name
353 * must_exist is pretty self explanatory
354 * buff is a buffer to write the path into, if NULL allocate
355 * size is the size of buff
357 * returns the path or NULL if either:
358 * must_exist is non-zero and the path doesn't exist
359 * buff is not NULL and too small to hold the full path
361 char* path_get(char* type
, char* file
, int must_exist
, char* buff
, int size
)
368 if (file
[0] == '/') {
369 return path_set(NULL
,file
,buff
,size
);
371 strcpy(rel_path
,file
);
372 }else if (!strcmp(type
,"game")) {
373 if (path
.game
&& (!must_exist
|| path_check(path
.game
,file
)))
374 return path_set(path
.game
,file
,buff
,size
);
376 }else if (!strcmp(type
,"screenshot")) {
377 if (path
.screenshot
) {
378 return path_set(path
.screenshot
,file
,buff
,size
);
379 }else if (path
.home
) {
380 return path_set(path
.home
,file
,buff
,size
);
381 }else if (path
.data_user
) {
382 return path_set(path
.data_user
,file
,buff
,size
);
383 }else if (path
.data_custom
) {
384 return path_set(path
.data_custom
,file
,buff
,size
);
387 }else if (!strcmp(type
,"config")) {
388 if (path
.config
&& (!must_exist
|| path_check(path
.config
,file
)))
389 return path_set(path
.config
,file
,buff
,size
);
391 }else if (!strcmp(type
,"model")) {
392 snprintf(rel_path
,1024,"models/%s",file
);
393 }else if (!strcmp(type
,"texture")) {
394 snprintf(rel_path
,1024,"textures/%s",file
);
395 }else if (!strcmp(type
,"shader")) {
396 snprintf(rel_path
,1024,"shaders/%s",file
);
397 }else if (!strcmp(type
,"html")) {
398 snprintf(rel_path
,1024,"html/%s",file
);
399 }else if (!strcmp(type
,"skin")) {
400 snprintf(rel_path
,1024,"textures/skins/%s",file
);
401 }else if (!strcmp(type
,"sound")) {
402 snprintf(rel_path
,1024,"sounds/%s",file
);
403 }else if (!strcmp(type
,"font")) {
404 snprintf(rel_path
,1024,"fonts/%s",file
);
405 }else if (!strncmp(type
,"translation-",12)) {
406 char* lang
= type
+12;
407 type
= "translation";
408 snprintf(rel_path
,1024,"locale/%s/%s",lang
,file
);
410 strcpy(rel_path
,file
);
413 /* check from data_path */
414 if (path
.data_custom
) {
415 if (path_check(path
.data_custom
,rel_path
))
416 return path_set(path
.data_custom
,rel_path
,buff
,size
);
419 /* check from user data directory */
420 if (path
.data_user
) {
421 if (path_check(path
.data_user
,rel_path
))
422 return path_set(path
.data_user
,rel_path
,buff
,size
);
425 /* check from default data directory */
427 if (path_check(path
.data
,rel_path
))
428 return path_set(path
.data
,rel_path
,buff
,size
);
431 /* check from default data directory */
432 if (path
.data_global
) {
433 if (path_check(path
.data_global
,rel_path
))
434 return path_set(path
.data_global
,rel_path
,buff
,size
);
441 return path_set(path
.data
,rel_path
,buff
,size
);
443 return path_set(path
.data_user
,rel_path
,buff
,size
);
444 if (path
.data_custom
)
445 return path_set(path
.data_custom
,rel_path
,buff
,size
);
451 * check if a path exists
454 * 0 if path does not exist
455 * 1 if path exists and is a file
456 * 2 if path exists and is a directory
458 int path_exists(char* path
)
460 return path_check(NULL
,path
);
464 * create the full path for the type
465 * assumes that files have a dot somewhere in their name
467 * if file is NULL, creates the base path for the type
468 * if file contains a dot, creates the base path for the type, and
469 * an empty file along with any subdirectories
470 * if file does not contain a dot, creates the base path for the
472 * if type is NULL, file must be an absolute path
473 * returns 0 if successful
475 int path_create(char* type
, char* file
)
481 if (!path_get(type
,file
,0,path
,2048))
485 char* b
= strrchr(file
,'/');
493 fn
= strrchr(path
,'/');
501 if (dir_create(path
))
518 /* removes (recursively) the last node in the full path of <type>/file */
519 int path_remove(char* type
, char* file
)
522 if (!path_get(type
,file
,1,path
,2048))
526 DWORD attributes
= GetFileAttributes(path
);
528 /* delete if it's a file, or call recursive delete if a directory */
529 if (attributes
== INVALID_FILE_ATTRIBUTES
)
532 if (attributes
== FILE_ATTRIBUTE_DIRECTORY
) {
537 list
= path_dirlist(NULL
,path
);
538 for (n
=list
; n
!= NULL
&& r
== 0; n
=n
->next
) {
539 if (snprintf(fpath
,2048,"%s/%s",path
,n
->name
) >= 2048)
541 if (path_remove(NULL
,fpath
))
544 while ((n
= list_pop(&list
))) {
550 if (RemoveDirectory(path
))
553 if (DeleteFile(path
))
557 if (path
[0] != '/' || path
[1] == 0)
559 /* file tree walk, calls the unlink_cb function on every file/directory */
560 return nftw(path
, unlink_cb
, 64, FTW_DEPTH
| FTW_PHYS
);
565 dirlist_t
*path_dirlist(char* type
, char* file
)
568 dirlist_t
*list
= NULL
;
572 WIN32_FIND_DATA FindFileData
;
573 HANDLE hFind
= INVALID_HANDLE_VALUE
;
581 if (!path_get(type
,file
,1,path
,2048))
584 DirSpec
= malloc(MAX_PATH
);
588 }else if (strlen(path
) > (MAX_PATH
-2)) {
592 sprintf(DirSpec
, "%s\\*", path
);
594 hFind
= FindFirstFile(DirSpec
, &FindFileData
);
596 if (hFind
== INVALID_HANDLE_VALUE
) {
599 nn
.name
= FindFileData
.cFileName
;
600 nn
.dir
= FindFileData
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
;
601 if (n
->name
[0] != '.' || (n
->name
[1] && strcmp(n
->name
,".."))) {
602 n
= malloc(sizeof(dirlist_t
));
603 n
->name
= strdup(nn
.name
);
605 list
= list_push(&list
,n
);
608 while (FindNextFile(hFind
, &FindFileData
) != 0) {
609 nn
.name
= FindFileData
.cFileName
;
610 nn
.dir
= FindFileData
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
;
611 if (n
->name
[0] != '.' || (n
->name
[1] && strcmp(n
->name
,".."))) {
612 n
= malloc(sizeof(dirlist_t
));
613 n
->name
= strdup(nn
.name
);
615 list
= list_push(&list
,n
);
619 dwError
= GetLastError();
621 if (dwError
!= ERROR_NO_MORE_FILES
)
628 if (retval
!= 0 && list
) {
629 while ((n
= list_pop(&list
))) {
639 while ((dirp
= readdir(dp
)) != NULL
) {
640 if (dirp
->d_name
[0] == '.' && (!dirp
->d_name
[1] || !strcmp(dirp
->d_name
,"..")))
642 n
= malloc(sizeof(dirlist_t
));
643 n
->name
= strdup(dirp
->d_name
);
644 if (dirp
->d_type
== DT_DIR
) {
649 list
= list_push(&list
,n
);