1 #include <string.h>
2 #include <lua.h>
3 #include <lauxlib.h>
4 #include <stdlib.h>
5 #include <stdio.h>
6 #include <time.h>
7 #include <errno.h>
8
9 #include "platform.h"
10 #include "path.h"
11 #include "context.h"
12 #include "session.h"
13 #include "support.h"
14
15 #ifdef BAM_FAMILY_BEOS
16 #include <sched.h>
17 #endif
18
19 #ifdef BAM_FAMILY_WINDOWS
20 /* windows code */
21 #define WIN32_LEAN_AND_MEAN
22 #define VC_EXTRALEAN
23 #include <windows.h>
24 #include <sys/types.h>
25 #include <sys/stat.h>
26 #include <sys/utime.h>
27 #include <signal.h>
28 #include <direct.h> /* _mkdir */
29
list_directory(const char * path,void (* callback)(const char * filename,int dir,void * user),void * user)30 static void list_directory(const char *path, void (*callback)(const char *filename, int dir, void *user), void *user)
31 {
32 WIN32_FIND_DATA finddata;
33 HANDLE handle;
34 char buffer[1024*2];
35 char *startpoint;
36
37 if(path[0])
38 {
39 strcpy(buffer, path);
40 strcat(buffer, "/*");
41 startpoint = buffer + strlen(path)+1;
42 }
43 else
44 {
45 strcpy(buffer, "*");
46 startpoint = buffer;
47 }
48
49 handle = FindFirstFileA(buffer, &finddata);
50
51 if (handle == INVALID_HANDLE_VALUE)
52 return;
53
54 /* add all the entries */
55 do
56 {
57 strcpy(startpoint, finddata.cFileName);
58 if(finddata.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)
59 callback(buffer, 1, user);
60 else
61 callback(buffer, 0, user);
62 } while (FindNextFileA(handle, &finddata));
63
64 FindClose(handle);
65 }
66
67 /* signals. should be moved to platform.c or similar? */
install_signals(void (* abortsignal)(int))68 void install_signals(void (*abortsignal)(int))
69 {
70 signal(SIGINT, abortsignal);
71 signal(SIGBREAK, abortsignal);
72 }
73
74 static CRITICAL_SECTION criticalsection;
platform_init()75 void platform_init()
76 {
77 InitializeCriticalSection(&criticalsection);
78
79 /* this environment variable is set by Microsoft Visual Studio
80 when building. It causes cl.exe to redirect it's output to
81 the specified pipe id. this causes loads of problems with
82 output.
83 */
84 SetEnvironmentVariable("VS_UNICODE_OUTPUT", NULL);
85 }
86
platform_shutdown()87 void platform_shutdown() { DeleteCriticalSection(&criticalsection); }
criticalsection_enter()88 void criticalsection_enter() { EnterCriticalSection(&criticalsection); }
criticalsection_leave()89 void criticalsection_leave() { LeaveCriticalSection(&criticalsection); }
90
threads_create(void (* threadfunc)(void *),void * u)91 void *threads_create(void (*threadfunc)(void *), void *u)
92 {
93 return CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)threadfunc, u, 0, NULL);
94 }
95
threads_join(void * thread)96 void threads_join(void *thread)
97 {
98 WaitForSingleObject((HANDLE)thread, INFINITE);
99 }
100
threads_yield()101 void threads_yield()
102 {
103 Sleep(1);
104 }
105
plugin_load(const char * filename)106 PLUGINFUNC plugin_load(const char *filename)
107 {
108 char buffer[MAX_PATH_LENGTH];
109 HMODULE handle;
110 FARPROC func;
111
112 _snprintf(buffer, sizeof(buffer), "%s.dll", filename);
113
114 handle = LoadLibrary(buffer);
115 if(handle == NULL)
116 {
117 fprintf(stderr, "error loading plugin '%s'\n", buffer);
118 return NULL;
119 }
120
121 func = GetProcAddress(handle, "plugin_main");
122 if(func == NULL)
123 {
124 CloseHandle(handle);
125 fprintf(stderr, "error fetching plugin main from '%s'\n", buffer);
126 return NULL;
127 }
128
129 return (PLUGINFUNC)func;
130 }
131
132 #else
133 #define D_TYPE_HACK
134
135 #ifdef D_TYPE_HACK
136 #define __USE_BSD
137 #endif
138
139 #include <dirent.h>
140 #include <dlfcn.h>
141 #include <unistd.h>
142 #include <sys/types.h>
143 #include <sys/signal.h>
144 #include <sys/stat.h>
145 #include <utime.h>
146 #include <pthread.h>
147
148 /* disable D_TYPE_HACK if we don't have support for it */
149 #if !defined(DT_DIR) || !defined(DT_UNKNOWN)
150 #undef D_TYPE_HACK
151 #endif
152
list_directory(const char * path,void (* callback)(const char * filename,int dir,void * user),void * user)153 static void list_directory(const char *path, void (*callback)(const char *filename, int dir, void *user), void *user)
154 {
155 DIR *dir;
156 struct dirent *entry;
157 struct stat info;
158 char buffer[1024];
159 char *startpoint;
160
161 if(*path == 0) /* special case for current directory */
162 {
163 dir = opendir(".");
164 startpoint = buffer;
165 }
166 else
167 {
168 dir = opendir(path);
169
170 /* get starting point and append a slash */
171 strcpy(buffer, path);
172 startpoint = buffer + strlen(buffer);
173 *startpoint = '/';
174 startpoint++;
175 }
176
177 if(!dir)
178 return;
179
180 while((entry = readdir(dir)) != NULL)
181 {
182 /* make the path absolute */
183 strcpy(startpoint, entry->d_name);
184 #ifdef D_TYPE_HACK
185 /* call the callback */
186 if(entry->d_type == DT_UNKNOWN)
187 {
188 /* do stat to obtain if it's a directory or not */
189 stat(buffer, &info);
190 if(S_ISDIR(info.st_mode))
191 callback(buffer, 1, user);
192 else
193 callback(buffer, 0, user);
194 }
195 else if(entry->d_type == DT_DIR)
196 callback(buffer, 1, user);
197 else
198 callback(buffer, 0, user);
199 #else
200 /* do stat to obtain if it's a directory or not */
201 stat(buffer, &info);
202 if(S_ISDIR(info.st_mode))
203 callback(buffer, 1, user);
204 else
205 callback(buffer, 0, user);
206 #endif
207 }
208
209 closedir(dir);
210 }
211
212 /* signals. should be moved to platform.c or similar? */
install_signals(void (* abortsignal)(int))213 void install_signals(void (*abortsignal)(int))
214 {
215 signal(SIGHUP, abortsignal);
216 signal(SIGINT, abortsignal);
217 signal(SIGKILL, abortsignal);
218 }
219
220 static pthread_mutex_t lock_mutex = PTHREAD_MUTEX_INITIALIZER;
221
platform_init()222 void platform_init() {}
platform_shutdown()223 void platform_shutdown() {}
criticalsection_enter()224 void criticalsection_enter() { pthread_mutex_lock(&lock_mutex); }
criticalsection_leave()225 void criticalsection_leave() { pthread_mutex_unlock(&lock_mutex); }
226
threads_create(void (* threadfunc)(void *),void * u)227 void *threads_create(void (*threadfunc)(void *), void *u)
228 {
229 pthread_t id;
230 pthread_create(&id, NULL, (void *(*)(void*))threadfunc, u);
231 return (void*)id;
232 }
233
threads_join(void * thread)234 void threads_join(void *thread)
235 {
236 pthread_join((pthread_t)thread, NULL);
237 }
238
threads_yield()239 void threads_yield()
240 {
241 sched_yield();
242 }
243
plugin_load(const char * filename)244 PLUGINFUNC plugin_load(const char *filename)
245 {
246 char buffer[MAX_PATH_LENGTH];
247 const char *error;
248 void *handle;
249 union
250 {
251 PLUGINFUNC func;
252 void *ptr;
253 } func;
254
255 if(strlen(filename) > sizeof(buffer) - 10)
256 return (PLUGINFUNC)0;
257
258 strcpy(buffer, "./");
259 strcat(buffer, filename);
260 strcpy(buffer, ".so");
261
262 handle = dlopen(buffer, RTLD_LAZY);
263 if(!handle)
264 {
265 fputs(dlerror(), stderr);
266 fputs("\n", stderr);
267
268 return NULL;
269 }
270
271 func.ptr = dlsym(handle, "plugin_main");
272 error = dlerror();
273
274 if(error)
275 {
276 fputs(error, stderr);
277 fputs("\n", stderr);
278 dlclose(handle);
279 return NULL;
280 }
281
282 return func.func;
283 }
284
285 #endif
286
timestamp()287 time_t timestamp() { return time(NULL); }
288
file_timestamp(const char * filename)289 time_t file_timestamp(const char *filename)
290 {
291 #ifdef BAM_PLATFORM_MACOSX
292 /* Mac OS X version */
293 struct stat s;
294 if(stat(filename, &s) == 0)
295 return s.st_mtimespec.tv_sec;
296 return 0;
297
298 #else
299 /* *NIX version and windows version*/
300 struct stat s;
301 if(stat(filename, &s) == 0)
302 return s.st_mtime;
303 return 0;
304 #endif
305 }
306
file_createdir(const char * path)307 int file_createdir(const char *path)
308 {
309 int r;
310 #ifdef BAM_FAMILY_WINDOWS
311 r = _mkdir(path);
312 #else
313 r = mkdir(path, 0755);
314 #endif
315 if(r == 0 || errno == EEXIST)
316 return 0;
317 return -1;
318 }
319
file_touch(const char * filename)320 void file_touch(const char *filename)
321 {
322 #ifdef BAM_FAMILY_WINDOWS
323 _utime(filename, NULL);
324 #else
325 utime(filename, NULL);
326 #endif
327 }
328
329 #ifdef BAM_FAMILY_WINDOWS
passthru(FILE * fp)330 static void passthru(FILE *fp)
331 {
332 while(1)
333 {
334 char buffer[1024*4];
335 size_t num_bytes = fread(buffer, 1, sizeof(buffer), fp);
336 if(num_bytes <= 0)
337 break;
338 criticalsection_enter();
339 fwrite(buffer, 1, num_bytes, stdout);
340 criticalsection_leave();
341 }
342 }
343 #endif
344
run_command(const char * cmd,const char * filter)345 int run_command(const char *cmd, const char *filter)
346 {
347 int ret;
348 #ifdef BAM_FAMILY_WINDOWS
349 FILE *fp = _popen(cmd, "r");
350
351 if(!fp)
352 return -1;
353
354 if(filter && *filter == 'F')
355 {
356 /* first filter match */
357 char buffer[1024];
358 size_t total;
359 size_t matchlen;
360 size_t numread;
361
362 /* skip first letter */
363 filter++;
364 matchlen = strlen(filter);
365 total = 0;
366
367 while(1)
368 {
369 numread = fread(buffer+total, 1, matchlen-total, fp);
370 if(numread <= 0)
371 {
372 /* done or error, flush and exit */
373 fwrite(buffer, 1, total, stdout);
374 break;
375 }
376
377 /* accumelate the bytes read */
378 total += numread;
379
380 if(total >= matchlen)
381 {
382 /* check if it matched */
383 if(memcmp(buffer, filter, matchlen) == 0)
384 {
385 /* check for line ending */
386 char t = fgetc(fp);
387 if(t == '\r')
388 {
389 /* this can be CR or CR/LF */
390 t = fgetc(fp);
391 if(t != '\n')
392 {
393 /* not a CR/LF */
394 fputc(t, stdout);
395 }
396 }
397 else if(t == '\n')
398 {
399 /* normal LF line ending */
400 }
401 else
402 {
403 /* no line ending */
404 fputc(t, stdout);
405 }
406 }
407 else
408 {
409 fwrite(buffer, 1, total, stdout);
410 }
411
412 passthru(fp);
413 break;
414 }
415
416 }
417 }
418 else
419 {
420 /* no filter */
421 passthru(fp);
422 }
423
424 ret = _pclose(fp);
425 #else
426 ret = system(cmd);
427 #endif
428 if(session.verbose)
429 printf("%s: ret=%d %s\n", session.name, ret, cmd);
430 return ret;
431 }
432
433 /* general */
file_exist(const char * filename)434 int file_exist(const char *filename)
435 {
436 struct stat s;
437 if(stat(filename, &s) == 0)
438 return 1;
439 return 0;
440 }
441
442 /* list directory functionallity */
443 typedef struct
444 {
445 lua_State *lua;
446 int i;
447 } LISTDIR_CALLBACK_INFO;
448
listdir_callback(const char * filename,int dir,void * user)449 static void listdir_callback(const char *filename, int dir, void *user)
450 {
451 LISTDIR_CALLBACK_INFO *info = (LISTDIR_CALLBACK_INFO *)user;
452 lua_pushstring(info->lua, filename);
453 lua_rawseti(info->lua, -2, info->i++);
454 }
455
lf_listdir(lua_State * L)456 int lf_listdir(lua_State *L)
457 {
458 LISTDIR_CALLBACK_INFO info;
459 info.lua = L;
460 info.i = 1;
461
462 /* create the table */
463 lua_newtable(L);
464
465 /* add all the entries */
466 if(strlen(lua_tostring(L, 1)) < 1)
467 list_directory(context_get_path(L), listdir_callback, &info);
468 else
469 {
470 char buffer[1024];
471 path_join(context_get_path(L), -1, lua_tostring(L,1), -1, buffer, sizeof(buffer));
472 list_directory(buffer, listdir_callback, &info);
473 }
474
475 return 1;
476 }
477
478 /* collect functionallity */
479 enum
480 {
481 COLLECTFLAG_FILES=1,
482 COLLECTFLAG_DIRS=2,
483 COLLECTFLAG_HIDDEN=4,
484 COLLECTFLAG_RECURSIVE=8
485 };
486
487 typedef struct
488 {
489 int path_len;
490 const char *start_str;
491 int start_len;
492
493 const char *end_str;
494 int end_len;
495
496 lua_State *lua;
497 int i;
498 int flags;
499 } COLLECT_CALLBACK_INFO;
500
501 static void run_collect(COLLECT_CALLBACK_INFO *info, const char *input);
502
collect_callback(const char * filename,int dir,void * user)503 static void collect_callback(const char *filename, int dir, void *user)
504 {
505 COLLECT_CALLBACK_INFO *info = (COLLECT_CALLBACK_INFO *)user;
506 const char *no_pathed = filename + info->path_len;
507 int no_pathed_len = strlen(no_pathed);
508
509 /* don't process . and .. paths */
510 if(filename[0] == '.')
511 {
512 if(filename[1] == 0)
513 return;
514 if(filename[1] == '.' && filename[2] == 0)
515 return;
516 }
517
518 /* don't process hidden stuff if not wanted */
519 if(no_pathed[0] == '.' && !(info->flags&COLLECTFLAG_HIDDEN))
520 return;
521
522 do
523 {
524 /* check end */
525 if(info->end_len > no_pathed_len || strcmp(no_pathed+no_pathed_len-info->end_len, info->end_str))
526 break;
527
528 /* check start */
529 if(info->start_len && strncmp(no_pathed, info->start_str, info->start_len))
530 break;
531
532 /* check dir vs search param */
533 if(!dir && info->flags&COLLECTFLAG_DIRS)
534 break;
535
536 if(dir && info->flags&COLLECTFLAG_FILES)
537 break;
538
539 /* all criterias met, push the result */
540 lua_pushstring(info->lua, filename);
541 lua_rawseti(info->lua, -2, info->i++);
542 } while(0);
543
544 /* recurse */
545 if(dir && info->flags&COLLECTFLAG_RECURSIVE)
546 {
547 char recursepath[1024];
548 COLLECT_CALLBACK_INFO recurseinfo = *info;
549 strcpy(recursepath, filename);
550 strcat(recursepath, "/");
551 strcat(recursepath, info->start_str);
552 run_collect(&recurseinfo, recursepath);
553 info->i = recurseinfo.i;
554 }
555 }
556
run_collect(COLLECT_CALLBACK_INFO * info,const char * input)557 static void run_collect(COLLECT_CALLBACK_INFO *info, const char *input)
558 {
559 char dir[1024];
560 int dirlen = 0;
561
562 /* get the directory */
563 path_directory(input, dir, sizeof(dir));
564 dirlen = strlen(dir);
565 info->path_len = dirlen+1;
566
567 /* set the start string */
568 if(dirlen)
569 info->start_str = input + dirlen + 1;
570 else
571 info->start_str = input;
572
573 for(info->start_len = 0; info->start_str[info->start_len]; info->start_len++)
574 {
575 if(info->start_str[info->start_len] == '*')
576 break;
577 }
578
579 /* set the end string */
580 if(info->start_str[info->start_len])
581 info->end_str = info->start_str + info->start_len + 1;
582 else
583 info->end_str = info->start_str + info->start_len;
584 info->end_len = strlen(info->end_str);
585
586 /* search the path */
587 list_directory(dir, collect_callback, info);
588 }
589
collect(lua_State * L,int flags)590 static int collect(lua_State *L, int flags)
591 {
592 int n = lua_gettop(L);
593 int i;
594 COLLECT_CALLBACK_INFO info;
595
596 if(n < 1)
597 luaL_error(L, "collect: incorrect number of arguments");
598
599 /* create the table */
600 lua_newtable(L);
601
602 /* set common info */
603 info.lua = L;
604 info.i = 1;
605 info.flags = flags;
606
607 /* start processing the input strings */
608 for(i = 1; i <= n; i++)
609 {
610 const char *input = lua_tostring(L, i);
611
612 if(!input)
613 continue;
614
615 run_collect(&info, input);
616 }
617
618 return 1;
619 }
620
lf_collect(lua_State * L)621 int lf_collect(lua_State *L) { return collect(L, COLLECTFLAG_FILES); }
lf_collectrecursive(lua_State * L)622 int lf_collectrecursive(lua_State *L) { return collect(L, COLLECTFLAG_FILES|COLLECTFLAG_RECURSIVE); }
lf_collectdirs(lua_State * L)623 int lf_collectdirs(lua_State *L) { return collect(L, COLLECTFLAG_DIRS); }
lf_collectdirsrecursive(lua_State * L)624 int lf_collectdirsrecursive(lua_State *L) { return collect(L, COLLECTFLAG_DIRS|COLLECTFLAG_RECURSIVE); }
625
626 /* */
627 #ifdef BAM_FAMILY_WINDOWS
628 /* on windows, we need to handle that filenames with mixed casing are the same.
629 to solve this we have this table that converts all uppercase letters.
630 in addition to this, we also convert all '\' to '/' to remove that
631 ambiguity
632 */
633 static const unsigned char tolower_table[256] = {
634 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
635 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
636 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
637 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
638 64, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
639 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 91, '/', 93, 94, 95,
640 96, 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
641 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 123, 124, 125, 126, 127,
642 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
643 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159,
644 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175,
645 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191,
646 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207,
647 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223,
648 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239,
649 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255};
650
string_hash_add(unsigned int h,const char * str_in)651 unsigned int string_hash_add(unsigned int h, const char *str_in)
652 {
653 const unsigned char *str = (const unsigned char *)str_in;
654 for (; *str; str++)
655 h = 31*h + tolower_table[*str];
656 return h;
657 }
658 #else
659 /* normal unix version */
string_hash_add(unsigned int h,const char * str)660 unsigned int string_hash_add(unsigned int h, const char *str)
661 {
662 for (; *str; str++)
663 h = 31*h + *str;
664 return h;
665 }
666 #endif
667
string_hash(const char * str_in)668 unsigned int string_hash(const char *str_in)
669 {
670 return string_hash_add(0, str_in);
671 }
672