1 /*
2  * Copyright 2014-2016, Björn Ståhl
3  * License: 3-Clause BSD, see COPYING file in arcan source repository.
4  * Reference: http://arcan-fe.com
5  */
6 
7 #include <stdlib.h>
8 #include <stdint.h>
9 #include <stdbool.h>
10 #include <stdio.h>
11 #include <libgen.h>
12 #include <string.h>
13 #include <math.h>
14 #include <assert.h>
15 #include <ctype.h>
16 
17 #include <sys/stat.h>
18 #include <sys/types.h>
19 #include <unistd.h>
20 
21 #include <arcan_math.h>
22 #include <arcan_general.h>
23 
arcan_isdir(const char * fn)24 bool arcan_isdir(const char* fn)
25 {
26 	struct stat buf;
27 	bool rv = false;
28 
29 	if (fn == NULL)
30 			return false;
31 
32 	if (stat(fn, &buf) == 0)
33 		rv = S_ISDIR(buf.st_mode);
34 
35 	return rv;
36 }
37 
arcan_isfile(const char * fn)38 bool arcan_isfile(const char* fn)
39 {
40 	struct stat buf;
41 	bool rv = false;
42 
43 	if (fn == NULL)
44 		return false;
45 
46 	if (stat(fn, &buf) == 0)
47 		rv = S_ISREG(buf.st_mode) || S_ISFIFO(buf.st_mode) || S_ISSOCK(buf.st_mode);
48 
49 	return rv;
50 }
51 
52 static char* pathks[] = {
53 	"path_appl",
54 	"path_resource",
55 	"path_appltemp",
56 	"path_state",
57 	"path_applbase",
58 	"path_applstore",
59 	"path_statebase",
60 	"path_font",
61 	"path_bin",
62 	"path_lib",
63 	"path_log",
64 	"path_script"
65 };
66 
67 static char* pinks[] = {
68 	"pin_appl",
69 	"pin_resource",
70 	"pin_appltemp",
71 	"pin_state",
72 	"pin_applbase",
73 	"pin_applstore",
74 	"pin_statebase",
75 	"pin_font",
76 	"pin_bin",
77 	"pin_lib",
78 	"pin_log",
79 	"pin_script"
80 };
81 
82 static char* envvs[] = {
83 	"ARCAN_APPLPATH",
84 	"ARCAN_RESOURCEPATH",
85 	"ARCAN_APPLTEMPPATH",
86 	"ARCAN_STATEPATH", /* will be ignored */
87 	"ARCAN_APPLBASEPATH",
88 	"ARCAN_APPLSTOREPATH",
89 	"ARCAN_STATEBASEPATH",
90 	"ARCAN_FONTPATH",
91 	"ARCAN_BINPATH",
92 	"ARCAN_LIBPATH",
93 	"ARCAN_LOGPATH",
94 	"ARCAN_SCRIPTPATH",
95 };
96 
97 static char* pinvs[] = {
98 	"ARCAN_APPLPIN",
99 	"ARCAN_RESOURCEPIN",
100 	"ARCAN_APPLTEMPPIN",
101 	"ARCAN_STATEPIN", /* will be ignored */
102 	"ARCAN_APPLBASEPIN",
103 	"ARCAN_APPLSTOREPIN",
104 	"ARCAN_STATEBASEPIN",
105 	"ARCAN_FONTPIN",
106 	"ARCAN_BINPIN",
107 	"ARCAN_LIBPIN",
108 	"ARCAN_LOGPIN",
109 	"ARCAN_SCRIPTPIN"
110 };
111 
alloc_cat(char * a,char * b)112 static char* alloc_cat(char* a, char* b)
113 {
114 	size_t a_sz = strlen(a);
115 	size_t b_sz = strlen(b);
116 	char* newstr = malloc(a_sz + b_sz + 1);
117 	newstr[a_sz + b_sz] = '\0';
118 	memcpy(newstr, a, a_sz);
119 	memcpy(newstr + a_sz, b, b_sz);
120 	return newstr;
121 }
122 
123 /*
124  * handle:
125  * abc [DEF] ghj [ARCAN_APPLPATH] klm [!.bla =>
126  * 	abc [DEF] ghj /usr/whatever klm [!.bla
127  * abc => abc
128  * [ARCAN_APPLPATH] apa [ARCAN_APPLPATH] => /usr/whatever apa /usr/whatever
129  * [ARCAN_APPLPATH => [ARCAN_APPLPATH
130  */
rep_str(char * instr)131 static char* rep_str(char* instr)
132 {
133 	char* pos = instr;
134 	char* beg;
135 
136 	while(1){
137 rep:
138 		beg = strchr(pos, '[');
139 		if (!beg)
140 			return instr;
141 
142 		char* end = strchr(beg+1, ']');
143 		if (!end)
144 			return instr;
145 
146 /* counter abc [ [ARCAN_APPLPATH] */
147 		char* step;
148 		while ((step = strchr(beg+1, '[')) && step < end)
149 			beg = step;
150 
151 		*end = '\0';
152 
153 		for (size_t i = 0; i < sizeof(envvs)/sizeof(envvs[0]); i++){
154 			if (strcmp(envvs[i], beg+1) == 0){
155 				char* exp = arcan_expand_resource("", 1 << i);
156 				if (exp){
157 					*beg = '\0';
158 					char* newstr = alloc_cat(instr, exp);
159 					char* resstr = alloc_cat(newstr, end+1);
160 					free(instr);
161 					free(newstr);
162 					pos = instr = resstr;
163 				}
164 				arcan_mem_free(exp);
165 				goto rep; /* continue 2; */
166 			}
167 		}
168 
169 		*end = ']';
170 		pos = end;
171 	}
172 }
173 
arcan_expand_namespaces(char ** inargs)174 char** arcan_expand_namespaces(char** inargs)
175 {
176 	char** work = inargs;
177 
178 	while(*work){
179 		*work = rep_str(*work);
180 		work++;
181 	}
182 
183 	return inargs;
184 }
185 
binpath_unix()186 static char* binpath_unix()
187 {
188 	char* binpath = NULL;
189 
190 	if (arcan_isfile( "./arcan_frameserver") )
191 		binpath = strdup("./arcan_frameserver" );
192 	else if (arcan_isfile( "/usr/local/bin/arcan_frameserver"))
193 		binpath = strdup("/usr/local/bin/arcan_frameserver");
194 	else if (arcan_isfile( "/usr/bin/arcan_frameserver" ))
195 		binpath = strdup("/usr/bin/arcan_frameserver");
196 	else ;
197 
198 	return binpath;
199 }
200 
scriptpath_unix()201 static char* scriptpath_unix()
202 {
203 	char* scrpath = NULL;
204 
205 	if (arcan_isdir("/usr/local/share/arcan/scripts"))
206 		scrpath = "/usr/local/share/arcan/scripts";
207 	else if (arcan_isdir("/usr/share/arcan/scripts"))
208 		scrpath = "/usr/share/arcan/scripts";
209 	else
210 		;
211 	return scrpath;
212 }
213 
unix_find(const char * fname)214 static char* unix_find(const char* fname)
215 {
216 	char* res = NULL;
217 	char* pathtbl[] = {
218 		".",
219 		NULL, /* fill in with HOME */
220 		"/usr/local/share/arcan",
221 		"/usr/share/arcan",
222 		NULL /* NULL terminates */
223 	};
224 
225 	if (getenv("HOME")){
226 		size_t len = strlen( getenv("HOME") ) + 9;
227 		pathtbl[1] = malloc(len);
228 		snprintf(pathtbl[1], len, "%s/.arcan", getenv("HOME") );
229 	}
230 	else
231 		pathtbl[1] = strdup("");
232 
233 	size_t fn_l = strlen(fname);
234 	for (char** base = pathtbl; *base != NULL; base++){
235 		char buf[ fn_l + strlen(*base) + 2 ];
236 		snprintf(buf, sizeof(buf), "%s/%s", *base, fname);
237 
238 		if (arcan_isdir(buf)){
239 			res = strdup(buf);
240 			break;
241 		}
242 	}
243 
244 	free(pathtbl[1]);
245 	return res;
246 }
247 
248 /*
249  * This is set-up to mimic the behavior of previous arcan
250  * version as much as possible. For other, more controlled settings,
251  * this is a good function to replace.
252  */
arcan_set_namespace_defaults()253 void arcan_set_namespace_defaults()
254 {
255 	char* tmp = NULL;
256 	uintptr_t tag;
257 	cfg_lookup_fun get_config = platform_config_lookup(&tag);
258 
259 /*
260  * use environment variables as hard overrides
261  */
262 	for (int i = 0; i < sizeof( envvs ) / sizeof( envvs[0] ); i++){
263 		char* tmp;
264 		if (get_config(pathks[i], 0, &tmp, tag) && tmp){
265 			arcan_override_namespace(tmp, 1 << i);
266 			free(tmp);
267 		}
268 
269 		tmp = getenv(envvs[i]);
270 		if (tmp)
271 			arcan_override_namespace(tmp, 1 << i);
272 
273 		if (getenv(pinvs[i]) || get_config(pinks[i], 0, NULL, tag))
274 			arcan_pin_namespace(1 << i);
275 	}
276 
277 	arcan_softoverride_namespace(tmp = scriptpath_unix(), RESOURCE_SYS_SCRIPTS);
278 
279 /*
280  * legacy mapping from the < 0.5 days
281  */
282 
283 	arcan_softoverride_namespace(tmp = binpath_unix(), RESOURCE_SYS_BINS);
284 	free(tmp);
285 
286 	char* respath = unix_find("resources");
287 
288 	if (!respath)
289 		respath = arcan_expand_resource("", RESOURCE_APPL_SHARED);
290 
291 	if (respath){
292 		size_t len = strlen(respath);
293 		char debug_dir[ len + sizeof("/logs") ];
294 		char font_dir[ len + sizeof("/fonts") ];
295 
296 		snprintf(debug_dir, sizeof(debug_dir), "%s/logs", respath);
297 		snprintf(font_dir, sizeof(font_dir), "%s/fonts", respath);
298 
299 		arcan_softoverride_namespace(respath, RESOURCE_APPL_SHARED);
300 		arcan_softoverride_namespace(debug_dir, RESOURCE_SYS_DEBUG);
301 		arcan_softoverride_namespace(respath, RESOURCE_APPL_STATE);
302 		arcan_softoverride_namespace(font_dir, RESOURCE_SYS_FONT);
303 		arcan_mem_free(respath);
304 	}
305 
306 	char* scrpath = unix_find("appl");
307 
308 	if (scrpath){
309 		arcan_softoverride_namespace(scrpath, RESOURCE_SYS_APPLBASE);
310 		arcan_softoverride_namespace(scrpath, RESOURCE_SYS_APPLSTORE);
311 		arcan_mem_free(scrpath);
312 	}
313 
314 	tmp = arcan_expand_resource("", RESOURCE_SYS_APPLSTATE);
315 	if (!tmp){
316 		tmp = arcan_expand_resource("savestates", RESOURCE_APPL_SHARED);
317 		if (tmp)
318 			arcan_override_namespace(tmp, RESOURCE_SYS_APPLSTATE);
319 	}
320 
321 	arcan_mem_free(tmp);
322 }
323