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