1 /* lua includes */
2 #include <lua.h>
3 #include <lualib.h>
4 #include <lauxlib.h>
5 
6 #include <stdio.h>
7 #include <string.h>
8 #include <ctype.h>
9 
10 #include "platform.h"
11 
12 #define PATH_SEPARATOR '/'
13 
path_is_separator(char c)14 static unsigned path_is_separator(char c)
15 {
16 #if defined(BAM_FAMILY_WINDOWS)
17 	return c == '/' || c == '\\';
18 #else
19 	return c == '/';
20 #endif
21 }
22 
23 /* */
path_filename(const char * path)24 const char *path_filename(const char *path)
25 {
26 	const char *ret = path;
27 	const char *cur;
28 	for(cur = path; *cur; cur++)
29 	{
30 		if(path_is_separator(*cur))
31 			ret = cur+1;
32 	}
33 	return ret;
34 }
35 
36 /* */
path_directory(const char * path,char * directory,int size)37 int path_directory(const char *path, char *directory, int size)
38 {
39 	char *dest = directory;
40 	char *dest_end = directory+size-1;
41 	const char *read = path;
42 	const char *cur;
43 
44 	for(cur = path; *cur; cur++)
45 	{
46 		if(path_is_separator(*cur))
47 		{
48 			/* ok, copy the directory */
49 			for(; read != cur; read++, dest++)
50 			{
51 				if(dest == dest_end)
52 				{
53 					*dest =  0;
54 					return 1;
55 				}
56 				*dest = *read;
57 			}
58 		}
59 	}
60 
61 	*dest = 0;
62 	if(0)
63 		printf("path_directory:\n\tinput:'%s'\n\toutput:'%s'\n", path, directory);
64 	return 0;
65 }
66 
67 /* normalizes the path, it rewrites the path */
path_normalize(char * path)68 int path_normalize(char *path)
69 {
70 	char *dirs[128];
71 	int depth = 0;
72 	char *dstptr = path;
73 	char *srcptr = path;
74 
75 	/* add the start */
76 	dirs[0] = path;
77 	depth++;
78 
79 	while(1)
80 	{
81 		if(srcptr[0] == '.')
82 		{
83 			if(path_is_separator(srcptr[1]))
84 			{
85 				/* "./" case, just skip the data */
86 				srcptr += 2;
87 			}
88 			else if(srcptr[1] == '.')
89 			{
90 				/* found ".." in path */
91 				if(path_is_separator(srcptr[2]))
92 				{
93 					/* "../" case */
94 					if(depth == 1)
95 					{
96 						/* case where we are at the start so append ../ to the start of the string */
97 						dstptr[0] = '.';
98 						dstptr[1] = '.';
99 						dstptr[2] = PATH_SEPARATOR;
100 						dstptr += 3;
101 						srcptr += 3;
102 
103 						dirs[0] = dstptr;
104 					}
105 					else
106 					{
107 						/* normal case where we are in the middle like "a/b/../c" */
108 						depth--;
109 						dstptr = dirs[depth-1];
110 						srcptr += 3;
111 					}
112 				}
113 				else if(srcptr[2] == 0)
114 				{
115 					/* ".." case, .. at end of string */
116 					if(depth == 1)
117 					{
118 						dstptr[0] = '.';
119 						dstptr[1] = '.';
120 						dstptr += 2;
121 						srcptr += 2;
122 
123 						dirs[0] = dstptr;
124 					}
125 					else
126 					{
127 						depth--;
128 						dstptr = dirs[depth-1];
129 						srcptr += 2;
130 					}
131 				}
132 				else
133 				{
134 					/* "..?" case */
135 					return -1;
136 				}
137 			}
138 		}
139 		else
140 		{
141 			/* search for separator */
142 			while(!path_is_separator(srcptr[0]) && srcptr[0])
143 				*dstptr++ = *srcptr++;
144 
145 			if(srcptr[0] == 0)
146 			{
147 				/* end of string, zero terminate and return, strip ending '/' if it exists */
148 				if(dstptr != path && path_is_separator(dstptr[-1]))
149 					dstptr[-1] = 0;
150 				dstptr[0] = 0;
151 				return 0;
152 			}
153 			else if(path_is_separator(srcptr[0]))
154 			{
155 				/* store the point of this directory */
156 				*dstptr++ = *srcptr++;
157 				dirs[depth] = dstptr;
158 				depth++;
159 			}
160 			else
161 			{
162 				/* non reachable case */
163 				return -1;
164 			}
165 		}
166 	}
167 
168 	return 0;
169 }
170 
171 /* returns true if a path is absolute */
path_isabs(const char * path)172 int path_isabs(const char *path)
173 {
174 #if defined(BAM_FAMILY_WINDOWS)
175 	if(strlen(path) > 2 && isalpha(path[0]) && path[1] == ':' && path_is_separator(path[2]))
176 		return 1;
177 #elif defined(BAM_FAMILY_UNIX)
178 	if(path_is_separator(path[0]))
179 		return 1;
180 #elif defined(BAM_FAMILY_BEOS)
181 	if(path_is_separator(path[0]))
182 		return 1;
183 #else
184 	#error path_isabs(const char *path) not implemented on this platform
185 #endif
186 
187 	return 0;
188 }
189 
190 /* is it absolute and normalized? */
path_isnice(const char * path)191 int path_isnice(const char *path)
192 {
193 	/* check for initial "../../" */
194 	while(path[0] == '.')
195 	{
196 		if(path[1] == '.')
197 		{
198 			if(path_is_separator(path[2]))
199 			{
200 				/* found "../" case */
201 				path += 3;
202 			}
203 			else
204 				return 0;
205 		}
206 		else if(path_is_separator(path[1]))
207 			return 0;
208 		else
209 			break;
210 	}
211 
212 	while(path[0])
213 	{
214 		if(path_is_separator(path[0]))
215 		{
216 			/* check for // */
217 			if(path_is_separator(path[1]))
218 				return 0;
219 
220 			if(path[1] == '.')
221 			{
222 				/* check for /.. */
223 				if(path[2] == '.')
224 					return 0;
225 
226 				/* check for /./ */
227 				if(path_is_separator(path[2]))
228 					return 0;
229 			}
230 
231 			/* check so that the path doesn't end on / */
232 			if(path[1] == 0)
233 				return 0;
234 		}
235 		path++;
236 	}
237 
238 	return 1;
239 }
240 
241 /* return zero on success */
path_join(const char * base,int base_len,const char * extend,int extend_len,char * output,int size)242 int path_join(const char *base, int base_len, const char *extend, int extend_len, char *output, int size)
243 {
244 	int i;
245 	if(extend_len < 0)
246 		extend_len = strlen(extend);
247 
248 	if(path_isabs(extend))
249 	{
250 		/* just copy the extend path */
251 		if(extend_len+1 > size)
252 		{
253 			fprintf(stderr, "'%s' + '%s' results in a too long path\n", base, extend);
254 			return __LINE__;
255 		}
256 
257 		memcpy(output, extend, extend_len+1);
258 		path_normalize(output);
259 		return 0;
260 	}
261 
262 	if(base_len < 0)
263 		base_len = strlen(base);
264 
265 	/* +2 for separator and null terminator */
266 	if(base_len+extend_len+2 > size)
267 	{
268 		fprintf(stderr, "'%s' + '%s' results in a too long path\n", base, extend);
269 		return __LINE__;
270 	}
271 
272 	/* no base path, just use extend path then */
273 	if(base_len == 0)
274 	{
275 		memcpy(output, extend, extend_len+1);
276 		path_normalize(output);
277 		return 0;
278 	}
279 
280 	/* copy base path */
281 	memcpy(output, base, base_len);
282 
283 	/* append path separator if needed */
284 	if(!path_is_separator(base[base_len-1]))
285 	{
286 		output[base_len] = PATH_SEPARATOR;
287 		base_len++;
288 	}
289 
290 	/* append the extra path, and null-terminator*/
291 	for(i = 0; i < extend_len+1; i++)
292 		output[base_len+i] = extend[i];
293 
294 	/* normalize path and return success */
295 	path_normalize(output);
296 	return 0;
297 }
298 
299 /*  */
lf_path_join(lua_State * L)300 int lf_path_join(lua_State *L)
301 {
302 	char buffer[1024*2];
303 	int n = lua_gettop(L);
304 	int err;
305 	const char *base;
306 	const char *extend;
307 	size_t base_len, extend_len;
308 
309 	if(n != 2)
310 		luaL_error(L, "path_join: incorrect number of arguments");
311 
312 	luaL_checktype(L, 1, LUA_TSTRING);
313 	luaL_checktype(L, 2, LUA_TSTRING);
314 
315 	base = lua_tolstring(L, 1, &base_len);
316 	extend = lua_tolstring(L, 2, &extend_len);
317 	err = path_join(base, base_len, extend, extend_len, buffer, 2*1024);
318 	if(err != 0)
319 	{
320 		luaL_error(L, "path_join: error %d, couldn't join\n\t'%s'\n  and\n\t'%s'",
321 			err,
322 			lua_tostring(L, 1),
323 			lua_tostring(L, 2));
324 	}
325 
326 	lua_pushstring(L, buffer);
327 	return 1;
328 }
329 
330 /*  */
lf_path_isnice(lua_State * L)331 int lf_path_isnice(lua_State *L)
332 {
333 	int n = lua_gettop(L);
334 	const char *path = 0;
335 
336 	if(n != 1)
337 		luaL_error(L, "path_isnice: incorrect number of arguments");
338 
339 	luaL_checktype(L, 1, LUA_TSTRING);
340 
341 	path = lua_tostring(L, 1);
342 	lua_pushnumber(L, path_isnice(path));
343 	return 1;
344 }
345 
lf_path_normalize(lua_State * L)346 int lf_path_normalize(lua_State *L)
347 {
348 	int n = lua_gettop(L);
349 	const char *path = 0;
350 
351 	if(n != 1)
352 		luaL_error(L, "path_normalize: incorrect number of arguments");
353 
354 	luaL_checktype(L, 1, LUA_TSTRING);
355 
356 	path = lua_tostring(L, 1);
357 
358 	if(path_isnice(path))
359 	{
360 		/* path is ok */
361 		lua_pushstring(L, path);
362 	}
363 	else
364 	{
365 		/* normalize and return */
366 		char buffer[2*1024];
367 		strcpy(buffer, path);
368 		path_normalize(buffer);
369 		lua_pushstring(L, buffer);
370 	}
371 
372 	return 1;
373 }
374 
375 
path_ext(const char * filename)376 static const char *path_ext(const char *filename)
377 {
378 	const char *cur = filename;
379 	const char *ext = 0;
380 
381 	for(; *cur; cur++)
382 	{
383 		if(*cur == '.')
384 			ext = cur;
385 		if(path_is_separator(*cur))
386 			ext = (const char *)0x0;
387 	}
388 	if(!ext)
389 		return "";
390 	return ext+1;
391 }
392 
393 /*  */
lf_path_ext(lua_State * L)394 int lf_path_ext(lua_State *L)
395 {
396 	int n = lua_gettop(L);
397 	const char *path = 0;
398 	if(n < 1)
399 		luaL_error(L, "path_ext: incorrect number of arguments");
400 
401 	path = lua_tostring(L, 1);
402 	if(!path)
403 		luaL_error(L, "path_ext: argument is not a string");
404 
405 	lua_pushstring(L, path_ext(path));
406 	return 1;
407 }
408 
409 
410 /*  */
lf_path_base(lua_State * L)411 int lf_path_base(lua_State *L)
412 {
413 	int n = lua_gettop(L);
414 	size_t org_len;
415 	size_t new_len;
416 	size_t count = 0;
417 	const char *cur = 0;
418 	const char *path = 0;
419 	if(n < 1)
420 		luaL_error(L, "path_base: incorrect number of arguments");
421 
422 	path = lua_tolstring(L, 1, &org_len);
423 	if(!path)
424 		luaL_error(L, "path_base: argument is not a string");
425 
426 	/* cut off the ext */
427 	new_len = org_len;
428 	for(cur = path; *cur; cur++, count++)
429 	{
430 		if(*cur == '.')
431 			new_len = count;
432 		else if(path_is_separator(*cur))
433 			new_len = org_len;
434 	}
435 
436 	lua_pushlstring(L, path, new_len);
437 	return 1;
438 }
439 
440 
path_dir_length(const char * path)441 static int path_dir_length(const char *path)
442 {
443 	const char *cur = path;
444 	int total = 0;
445 	int len = -1;
446 	for(; *cur; cur++, total++)
447 	{
448 		if(path_is_separator(*cur))
449 			len = (int)(cur-path);
450 	}
451 
452 	if(len == -1)
453 		return 0;
454 	return len;
455 }
456 
457 /*  */
lf_path_dir(lua_State * L)458 int lf_path_dir(lua_State *L)
459 {
460 	char buffer[1024];
461 	int n = lua_gettop(L);
462 	const char *path = 0;
463 	if(n < 1)
464 		luaL_error(L, "path_dir: incorrect number of arguments");
465 
466 	path = lua_tostring(L, 1);
467 	if(!path)
468 		luaL_error(L, "path_dir: argument is not a string");
469 
470 	/* check if we can take the easy way out */
471 	if(path_isnice(path))
472 	{
473 		lua_pushlstring(L, path, path_dir_length(path));
474 		return 1;
475 	}
476 
477 	/* we must normalize the path as well */
478 	strncpy(buffer, path, sizeof(buffer));
479 	path_normalize(buffer);
480 	lua_pushlstring(L, buffer, path_dir_length(buffer));
481 	return 1;
482 }
483 
484 /*  */
lf_path_filename(lua_State * L)485 int lf_path_filename(lua_State *L)
486 {
487 	int n = lua_gettop(L);
488 	const char *path = 0;
489 	if(n < 1)
490 		luaL_error(L, "path_filename: incorrect number of arguments");
491 
492 	path = lua_tostring(L, 1);
493 
494 	if(!path)
495 		luaL_error(L, "path_filename: null name");
496 
497 	lua_pushstring(L, path_filename(path));
498 	return 1;
499 }
500