1 /*
2 Copyright 2010 Jared Krinke.
3 
4 Permission is hereby granted, free of charge, to any person obtaining a copy
5 of this software and associated documentation files (the "Software"), to deal
6 in the Software without restriction, including without limitation the rights
7 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 copies of the Software, and to permit persons to whom the Software is
9 furnished to do so, subject to the following conditions:
10 
11 The above copyright notice and this permission notice shall be included in
12 all copies or substantial portions of the Software.
13 
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 THE SOFTWARE.
21 */
22 
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <sys/stat.h>
26 #include <sys/types.h>
27 #include <errno.h>
28 #include <string.h>
29 #include <ctype.h>
30 #include <physfs.h>
31 
32 #include "r_defs.h"
33 #include "r_assert.h"
34 #include "r_string.h"
35 #include "r_file_system.h"
36 
37 const char *r_platform_newline = "\n";
38 
r_platform_create_directory(r_state_t * rs,const char * dir)39 r_status_t r_platform_create_directory(r_state_t *rs, const char *dir)
40 {
41     r_status_t status = (rs != NULL && dir != NULL) ? R_SUCCESS : R_F_INVALID_POINTER;
42     R_ASSERT(R_SUCCEEDED(status));
43 
44     if (R_SUCCEEDED(status))
45     {
46         int result = mkdir(dir, 0744);
47 
48         status = (result == 0) ? R_SUCCESS : R_FAILURE;
49 
50         if (R_FAILED(status) && errno == EEXIST)
51         {
52             status = R_S_ALREADY_EXISTS;
53         }
54     }
55 
56     return status;
57 }
58 
r_platform_setup_output(r_state_t * rs,const char * user_dir)59 r_status_t r_platform_setup_output(r_state_t *rs, const char *user_dir)
60 {
61     /* Just use stdout/stderr normally on UNIX */
62     return R_SUCCESS;
63 }
64 
r_platform_application_allocate_user_dir(r_state_t * rs,const char * application,char ** user_dir)65 r_status_t r_platform_application_allocate_user_dir(r_state_t *rs, const char *application, char **user_dir)
66 {
67     r_status_t status = (rs != NULL && application != NULL && user_dir!= NULL) ? R_SUCCESS : R_F_INVALID_POINTER;
68     R_ASSERT(R_SUCCEEDED(status));
69 
70     if (R_SUCCEEDED(status))
71     {
72         char *home = getenv("HOME");
73 
74         status = (home != NULL) ? R_SUCCESS : R_FAILURE;
75 
76         if (R_SUCCEEDED(status))
77         {
78             status = r_string_format_allocate(rs, user_dir, R_FILE_SYSTEM_MAX_PATH_LENGTH, R_FILE_SYSTEM_MAX_PATH_LENGTH, "%s/.%s", home, application);
79         }
80     }
81 
82     return status;
83 }
84 
r_platform_application_allocate_data_dirs(r_state_t * rs,const char * application_name,const char * data_dir_override,char *** data_dirs)85 r_status_t r_platform_application_allocate_data_dirs(r_state_t *rs, const char *application_name, const char *data_dir_override, char ***data_dirs)
86 {
87     /* Convert application name to lower case */
88     size_t application_name_length = strlen(application_name);
89     char *application_name_lower = (char*)malloc(application_name_length + 1);
90     r_status_t status = (application_name_lower != NULL) ? R_SUCCESS : R_F_OUT_OF_MEMORY;
91 
92     if (R_SUCCEEDED(status))
93     {
94         int c;
95 
96         for (c = 0; c < application_name_length; ++c)
97         {
98             application_name_lower[c] = tolower(application_name[c]);
99         }
100 
101         application_name_lower[c] = '\0';
102 
103         {
104             /* These are encoded with the lower-cased application name */
105             static const char *default_dirs[] = {
106                 "/usr/share/%s",
107                 "/usr/local/share/%s",
108                 "/usr/share/games/%s",
109                 "/usr/local/share/games/%s",
110             };
111 
112             /* Data directory search path:
113              *
114              * 1) Hard-coded override (user-supplied at compile time)
115              * 2) Default installation paths (these are just educated guesses)
116              * 3) PhysicsFS "base" dir (e.g. if the user runs from the build directory)
117              * (Followed by a NULL) */
118 
119             char **data_dirs_internal = (char**)calloc(R_ARRAY_SIZE(default_dirs) + 3, sizeof(char*));
120             r_status_t status = (data_dirs_internal != NULL) ? R_SUCCESS : R_F_OUT_OF_MEMORY;
121 
122             if (R_SUCCEEDED(status))
123             {
124                 /* First, use the hard-coded override */
125                 int j = 0;
126 
127                 status = r_string_format_allocate(rs, &data_dirs_internal[j++], R_FILE_SYSTEM_MAX_PATH_LENGTH, R_FILE_SYSTEM_MAX_PATH_LENGTH, "%s", data_dir_override);
128 
129                 /* Next, use the default paths */
130                 if (R_SUCCEEDED(status))
131                 {
132                     int i;
133 
134                     for (i = 0; R_SUCCEEDED(status) && i < R_ARRAY_SIZE(default_dirs); ++i)
135                     {
136                         status = r_string_format_allocate(rs, &data_dirs_internal[j++], R_FILE_SYSTEM_MAX_PATH_LENGTH, R_FILE_SYSTEM_MAX_PATH_LENGTH, default_dirs[i], application_name_lower);
137                     }
138                 }
139 
140                 /* As a last resort, use "<base>/Data" */
141                 if (R_SUCCEEDED(status))
142                 {
143                     status = r_string_format_allocate(rs, &data_dirs_internal[j++], R_FILE_SYSTEM_MAX_PATH_LENGTH, R_FILE_SYSTEM_MAX_PATH_LENGTH, "%sData", PHYSFS_getBaseDir());
144                 }
145 
146                 if (R_FAILED(status))
147                 {
148                     /* Free already-allocated paths */
149                     for (--j; j >= 0; --j)
150                     {
151                         free(data_dirs_internal[j]);
152                     }
153 
154                     free(data_dirs_internal);
155                 }
156             }
157 
158             if (R_SUCCEEDED(status))
159             {
160                 *data_dirs = data_dirs_internal;
161             }
162         }
163 
164         free(application_name_lower);
165     }
166 
167     return status;
168 }
169 
170