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