1 /* compiler.c++ -- written by Alexis WILKE for Made to Order Software Corp. (c) 2005-2009 */
2 
3 /*
4 
5 Copyright (c) 2005-2009 Made to Order Software Corp.
6 
7 Permission is hereby granted, free of charge, to any
8 person obtaining a copy of this software and
9 associated documentation files (the "Software"), to
10 deal in the Software without restriction, including
11 without limitation the rights to use, copy, modify,
12 merge, publish, distribute, sublicense, and/or sell
13 copies of the Software, and to permit persons to whom
14 the Software is furnished to do so, subject to the
15 following conditions:
16 
17 The above copyright notice and this permission notice
18 shall be included in all copies or substantial
19 portions of the Software.
20 
21 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
22 ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
23 LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
24 FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
25 EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
27 WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
28 ARISING FROM, OUT OF OR IN CONNECTION WITH THE
29 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30 SOFTWARE.
31 
32 */
33 
34 #include	"compiler.h"
35 
36 #ifdef WIN32
37 #include	<windows.h>
38 #endif
39 
40 
41 namespace sswf
42 {
43 namespace as
44 {
45 
46 #ifdef _MSVC
47 // replicate the Unix directory functions
48 struct DIRECTORY {
49 	const char              *path;
50 	struct _finddata_t      info;
51 	long                    handle;
52 };
53 
54 typedef struct DIRECTORY        DIR;
55 
56 
57 struct dirent {
58 	char                    d_name[128];
59 };
60 
opendir(const char * path)61 DIR *opendir(const char *path)
62 {
63 	static DIR	dir;
64 
65 	memset(&dir, 0, sizeof(dir));
66 	dir.path = path;
67 	dir.handle = -1;
68 
69 	return (&dir);
70 }
71 
72 
readdir(DIR * dir)73 struct dirent *readdir(DIR *dir)
74 {
75 	static struct dirent	entry;
76 
77 	long		err, len;
78 	char		*name;
79 
80 	memset(&entry, 0, sizeof(entry));
81 
82 	if(dir->handle == -1) {
83 		len = strlen(dir->path) + 3;
84 		name = new char[len];
85 		memcpy(name, dir->path, len - 3);
86 		name[len - 3] = '\\';
87 		name[len - 2] = '*';
88 		name[len - 1] = '\0';
89 		dir->handle = _findfirst(
90 				name,
91 				&dir->info);
92 		delete [] name;
93 		if(dir->handle < 0) {
94 			err = -1;
95 		}
96 		else {
97 			err = 0;
98 		}
99 	}
100 	else {
101 		err = _findnext(dir->handle,
102 				&dir->info);
103 	}
104 
105 	if(err < 0) {	/* end of directory */
106 		return (NULL);
107 	}
108 
109 	strncpy(entry.d_name, dir->info.name,
110 			sizeof(entry.d_name));
111 
112 	return (&entry);
113 }
114 
115 
closedir(DIR * dir)116 void closedir(DIR *dir)
117 {
118 	if(dir->handle != -1) {
119 		_findclose(dir->handle);
120 	}
121 
122 	return;
123 }
124 #endif	/* #if _MSVC */
125 
126 
127 /**********************************************************************/
128 /**********************************************************************/
129 /***  COMPILER CREATOR  ***********************************************/
130 /**********************************************************************/
131 /**********************************************************************/
132 
CreateCompiler(InputRetriever * input)133 Compiler *Compiler::CreateCompiler(InputRetriever *input)
134 {
135 	return new IntCompiler(input);
136 }
137 
138 
Version(void)139 const char *Compiler::Version(void)
140 {
141 	return TO_STR(SSWF_VERSION);
142 }
143 
144 
145 /**********************************************************************/
146 /**********************************************************************/
147 /***  INTERNAL COMPILER (Basics)  *************************************/
148 /**********************************************************************/
149 /**********************************************************************/
150 
151 
152 IntCompiler::rc_t	IntCompiler::g_rc;
153 NodePtr			IntCompiler::g_global_import;
154 NodePtr			IntCompiler::g_system_import;
155 NodePtr			IntCompiler::g_native_import;
156 
157 
IntCompiler(InputRetriever * input)158 IntCompiler::IntCompiler(InputRetriever *input)
159 {
160 	// TODO: under MS-Windows, we may want to use something such
161 	//	 as a SHGetFolder() call
162 	f_home = getenv("HOME");
163 
164 	//f_default_error_stream -- auto-init
165 	f_error_stream = &f_default_error_stream;
166 	//f_optimizer -- auto-init
167 	f_optimizer.SetErrorStream(f_default_error_stream);
168 	f_options = 0;
169 	f_input_retriever = input;
170 	//f_program -- auto-init
171 	f_time = time(NULL);
172 	f_err_flags = 0;
173 	//f_scope -- auto-init
174 	f_db = 0;
175 	f_db_size = 0;
176 	f_db_data = 0;
177 	f_db_count = 0;
178 	f_db_max = 0;
179 	f_db_packages = 0;
180 	f_mod_count = 0;
181 	f_mod_max = 0;
182 	f_modules = 0;
183 
184 	InternalImports();
185 }
186 
187 
~IntCompiler()188 IntCompiler::~IntCompiler()
189 {
190 	if(f_db != 0) {
191 		fclose(f_db);
192 	}
193 	delete [] f_db_data;
194 
195 	// delete packages we added in this session
196 	for(size_t idx = 0; idx < f_db_count; ++idx) {
197 		if(f_db_packages[idx] < f_db_data
198 		|| f_db_packages[idx] > f_db_data + f_db_size) {
199 			delete [] f_db_packages[idx];
200 		}
201 	}
202 
203 	delete [] f_db_packages;
204 }
205 
206 
SetInputRetriever(InputRetriever * retriever)207 InputRetriever *IntCompiler::SetInputRetriever(InputRetriever *retriever)
208 {
209 	InputRetriever *old(f_input_retriever);
210 
211 	f_input_retriever = retriever;
212 
213 	return old;
214 }
215 
216 
SetErrorStream(ErrorStream & error_stream)217 void IntCompiler::SetErrorStream(ErrorStream& error_stream)
218 {
219 	f_error_stream = &error_stream;
220 	f_optimizer.SetErrorStream(error_stream);
221 }
222 
223 
SetOptions(Options & options)224 void IntCompiler::SetOptions(Options& options)
225 {
226 	f_options = &options;
227 	f_optimizer.SetOptions(options);
228 }
229 
230 
231 
232 
233 
isspace(int c)234 bool IntCompiler::isspace(int c)
235 {
236 	return c == ' ' || c == '\t';
237 }
238 
239 
240 
241 
FindRC(const String & home,bool accept_if_missing)242 void IntCompiler::rc_t::FindRC(const String& home, bool accept_if_missing)
243 {
244 	// first try to find a place with a .rc
245 	// (at this time, these directories are kind of random...)
246 	static const char *directories[] = {
247 		// Mainly for Unices
248 		".",
249 		"include/sswf/scripts",
250 		"@include/sswf/scripts",	// For Win32
251 		".sswf",
252 		"~/.sswf",
253 		"~/sswf",
254 		"/etc",
255 		"/etc/sswf",
256 		"/usr/include/sswf/scripts",
257 		"/usr/share/sswf/scripts",
258 		0
259 	};
260 
261 	const char **dir, *d;
262 
263 	for(dir = directories; *dir != 0; ++dir) {
264 		d = *dir;
265 		if(*d == '@') {
266 			// by default I assume I cannot apply this function properly
267 			f_filename[0] = '\0';
268 #ifdef WIN32
269 			char		fullpath[MAX_PATH * 5];
270 
271 			fullpath[0] = '\0';
272 			DWORD sz = GetModuleFileNameA(NULL, fullpath, MAX_PATH * 5);
273 			if(sz < MAX_PATH * 5) {
274 				char *s = fullpath + strlen(fullpath);
275 				while(s > fullpath) {
276 					if(s[-1] == '\\') {
277 						s--;
278 						while(s > fullpath) {
279 							if(s[-1] == '\\') {
280 								s[0] = '\0';
281 								snprintf(f_filename, sizeof(f_filename),
282 									"%s%s\\sswf.rc", fullpath, *dir + 1);
283 								break;
284 							}
285 							s--;
286 						}
287 						break;
288 					}
289 					s--;
290 				}
291 			}
292 #endif
293 		}
294 		else if(*d == '~') {
295 			if(home.IsEmpty()) {
296 				// no valid HOME directory
297 				continue;
298 			}
299 			char buf[256];
300 			size_t sz = sizeof(buf);
301 			home.ToUTF8(buf, sz);
302 			snprintf(f_filename, sizeof(f_filename), "%s/%s/sswf.rc", buf, *dir + 1);
303 		}
304 		else {
305 			snprintf(f_filename, sizeof(f_filename), "%s/sswf.rc", *dir);
306 		}
307 		if(f_filename[0] != '\0') {
308 #ifdef WIN32
309 			// Win98 can have problems with / in filenames
310 			char *s = f_filename;
311 			while(*s != '\0') {
312 				if(*s == '/') {
313 					*s = '\\';
314 				}
315 				s++;
316 			}
317 #endif
318 			f_f = fopen(f_filename, "rb");
319 			if(f_f != 0) {
320 				break;
321 			}
322 		}
323 	}
324 	if(f_f == 0) {
325 		if(!accept_if_missing) {
326 			fprintf(stderr, "INSTALLATION ERROR: cannot find the sswf.rc file; it is usually put in "
327 #ifdef WIN32
328 				"include/sswf/scripts found under the folder of the sswf executable.\n"
329 #else
330 				"/etc/sswf/sswf.rc\n"
331 #endif
332 				);
333 			exit(1);
334 		}
335 		// if we want everything internal, we'll just use working defaults
336 		f_path = "include/sswf/scripts";
337 		f_db = "tmp/asc_packages.db";
338 		strcpy(f_filename, "internal.rc");
339 	}
340 }
341 
342 
ReadRC(void)343 void IntCompiler::rc_t::ReadRC(void)
344 {
345 	char buf[256], *s, *name, *param;
346 	int l, line, quote;
347 
348 	// if f_f is null, we already have the defaults
349 	// and f_input_retriever is not NULL
350 	if(f_f == 0) {
351 		return;
352 	}
353 
354 	line = 0;
355 	while(fgets(buf, sizeof(buf), f_f) != 0) {
356 		line++;
357 		s = buf;
358 		while(isspace(*s)) {
359 			s++;
360 		}
361 		if(*s == '#' || *s == '\n' || *s == '\0') {
362 			continue;
363 		}
364 		name = s;
365 		while(*s != '\0' && *s != '=' && !isspace(*s)) {
366 			s++;
367 		}
368 		l = s - name;
369 		while(isspace(*s)) {
370 			s++;
371 		}
372 		if(*s != '=') {
373 fprintf(stderr, "%s:%d: syntax error; expected an equal sign\n", f_filename, line);
374 			continue;
375 		}
376 		// skip the = and following spaces
377 		s++;
378 		while(isspace(*s)) {
379 			s++;
380 		}
381 		if(*s == '"' || *s == '\'') {
382 			quote = *s++;
383 			param = s;
384 			while(*s != '\0' && *s != quote && *s != '\n') {
385 				s++;
386 			}
387 		}
388 		else {
389 			param = s;
390 			while(*s != '\0' && *s != '\n') {
391 				s++;
392 			}
393 		}
394 		*s = '\0';
395 		if(l == 7 && strncmp(name, "version", 7) == 0) {
396 			// TODO: check that we understand this version
397 			continue;
398 		}
399 		if(l == 8 && strncmp(name, "asc_path", 8) == 0) {
400 //fprintf(stderr, "Got path! [%s]\n", param);
401 			f_path = param;
402 			continue;
403 		}
404 		if(l == 6 && strncmp(name, "asc_db", 6) == 0) {
405 			f_db = param;
406 			continue;
407 		}
408 	}
409 }
410 
411 
412 
413 
GetPackageFilename(const char * package_info)414 String IntCompiler::GetPackageFilename(const char *package_info)
415 {
416 	int		cnt;
417 
418 	cnt = 0;
419 	while(*package_info != '\0') {
420 		package_info++;
421 		if(package_info[-1] == ' ') {
422 			cnt++;
423 			if(cnt >= 3) {
424 				break;
425 			}
426 		}
427 	}
428 	if(*package_info != '"') {
429 		return "";
430 	}
431 	package_info++;
432 	const char *name = package_info;
433 	while(*package_info != '"' && *package_info != '\0') {
434 		package_info++;
435 	}
436 
437 	String result;
438 	result.FromUTF8(name, package_info - name);
439 
440 	return result;
441 }
442 
443 
444 /** \fn InputRetriever::Retrieve(const char *filename) = 0
445  *
446  * \brief Function you want to overload to provide your own input file to the loader.
447  *
448  * The IntCompiler::FindModule() function calls this function to retrieve an
449  * input file. The input file is used to load the module specified by
450  * \p filename.
451  *
452  * This is used by the libasc extension that enables you to put all the system
453  * ASC files in your binary and thus avoid you having to deal with a directory
454  * structure.
455  *
456  * \bug
457  * The returned pointer MUST be a class derived from Input and MUST be allocated.
458  * If the file cannot be opened by your retriever, then return NULL.
459  *
460  * \param[in] filename  The name of the module to be loaded
461  *
462  * \return NULL or an object derived from the Input class. You MUST allocate this object
463  * since the IntCompiler::FindModule() will delete it once the module was loaded.
464  */
465 
FindModule(const String & filename,NodePtr & result)466 bool IntCompiler::FindModule(const String& filename, NodePtr& result)
467 {
468 	int		i, j, p, r;
469 	module_t	*mod;
470 
471 	p = 0;		// avoid warnings
472 	j = f_mod_count;
473 	if(j < 4) {
474 		// lack of precision forces us to do this with 0 to 3 elements
475 		p = 0;
476 		while(p < j) {
477 			r = filename.Compare(f_modules[p].f_filename);
478 			if(r == 0) {
479 				result = f_modules[p].f_node;
480 				return true;
481 			}
482 			// when filename < f_modules.f_filename,
483 			// we won't find anything anymore; also
484 			// p is at the right place to add the
485 			// file!
486 			if(r < 0) {
487 				break;
488 			}
489 			p++;
490 		}
491 	}
492 	else {
493 		// this is really the binary search
494 		// (see the / 2 below?)
495 		i = 0;
496 		while(i < j) {
497 			p = i + (j - i) / 2;
498 			r = filename.Compare(f_modules[p].f_filename);
499 			if(r == 0) {
500 				result = f_modules[p].f_node;
501 				return true;
502 			}
503 			if(r > 0) {
504 				p++;
505 				i = p;
506 			}
507 			else {
508 				j = p;
509 			}
510 		}
511 	}
512 
513 // we couldn't find this entry, when n != 0 insert a new row
514 // otherwise try to load the module
515 	if(!result.HasNode()) {
516 		FileInput file_input;
517 		Input *in(NULL);
518 		char *fn = filename.GetUTF8();
519 		if(f_input_retriever != NULL) {
520 			in = f_input_retriever->Retrieve(fn);
521 		}
522 		if(in == NULL) {
523 			if(!file_input.Open(fn)) {
524 				fprintf(stderr, "FATAL ERROR: cannot open module file \"%s\".\n", fn);
525 				delete [] fn;
526 				exit(1);
527 			}
528 			in = &file_input;
529 		}
530 
531 		Parser *parser = Parser::CreateParser();
532 		if(f_options != 0) {
533 			parser->SetOptions(*f_options);
534 		}
535 		parser->SetInput(*in);
536 
537 		result = parser->Parse();
538 
539 		delete parser;
540 		if(in != &file_input) {
541 			// This is ugly, we should have a shared pointer or
542 			// some other feature to handle that one!
543 			delete in;
544 		}
545 
546 #if 0 && (defined(_DEBUG) || defined(DEBUG))
547 fprintf(stderr, "%s module:\n", fn);
548 result.Display(stderr);
549 #endif
550 
551 		if(!result.HasNode()) {
552 			fprintf(stderr, "FATAL ERROR: cannot compile module file \"%s\".\n", fn);
553 			delete [] fn;
554 			exit(1);
555 		}
556 
557 		delete [] fn;
558 	}
559 
560 	// we need to enlarge the f_db_packages buffer
561 	if(f_mod_count >= f_mod_max) {
562 		// enlarge buffer
563 		f_mod_max += 250;
564 		mod = new module_t[f_mod_max];
565 		for(i = 0; (size_t) i < f_mod_count; ++i) {
566 			mod[i] = f_modules[i];
567 		}
568 		delete [] f_modules;
569 		f_modules = mod;
570 	}
571 
572 	i = f_mod_count;
573 	while(i > p) {
574 		f_modules[i] = f_modules[i - 1];
575 		i--;
576 	}
577 	f_mod_count++;
578 
579 	f_modules[p].f_filename = filename;
580 	f_modules[p].f_node = result;
581 
582 	return true;
583 }
584 
585 
586 
LoadModule(const char * module,const char * file)587 NodePtr IntCompiler::LoadModule(const char *module, const char *file)
588 {
589 	char		buf[256];
590 	char		path[256];
591 	size_t		sz;
592 	NodePtr		result;
593 
594 	// create the name of the module
595 	sz = sizeof(path);
596 	g_rc.GetPath().ToUTF8(path, sz);
597 	sz = snprintf(buf, sizeof(buf), "%s/%s/%s", path, module, file);
598 	if(sz >= sizeof(buf)) {
599 		fprintf(stderr, "FATAL ERROR: filename too long; cannot load module.\n");
600 		exit(1);
601 	}
602 
603 	// search for the module; if not already loaded, load it
604 	FindModule(buf, result);
605 	return result;
606 }
607 
608 
ReadDB(void)609 void IntCompiler::ReadDB(void)
610 {
611 	Input		*in(NULL);
612 
613 	if(f_db != 0) {
614 		fclose(f_db);
615 		f_db = 0;
616 	}
617 
618 // the input retriever defines the packages database?
619 	if(f_input_retriever != NULL) {
620 		// if we have a retriever query the retriever for the database
621 		// in this case the database is read only (i.e. f_db remains set
622 		// to zero)
623 		in = f_input_retriever->Retrieve("asc_packages.db");
624 	}
625 	if(in != NULL) {
626 		f_db_size = in->GetSize();
627 		delete [] f_db_data;
628 		f_db_data = new char[f_db_size + 2];
629 		// TODO: we do not currently support UTF-8 here, only ASCII...
630 		for(size_t i = 0; i < f_db_size; ++i) {
631 			f_db_data[i] = in->GetC();
632 		}
633 		// we're done with the input
634 		delete in;
635 
636 		f_db_data[f_db_size] = '\0';
637 	}
638 	else {
639 // get resource filename if any
640 		String db = g_rc.GetDB();
641 		if(db.GetLength() == 0) {
642 			// internal default
643 			db = "~/.sswf/asc_packages.db";
644 		}
645 
646 // check for user path
647 		const long *path = db.Get();
648 		long len = db.GetLength();
649 		if(len > 1
650 		&& path[0] == '~'
651 		&& (path[1] == '/' || path[1] == '\\')) {
652 			String s = f_home;
653 			s.AppendStr(path + 1, len - 1);
654 			db = s;
655 		}
656 
657 // open the file
658 		char filename[256];
659 		size_t sz = sizeof(filename);
660 		db.ToUTF8(filename, sz);
661 		f_db = fopen(filename, "rb+");
662 		if(f_db == 0) {
663 			// if it fails, try to create possibly missing directories
664 			char *fn;
665 			fn = filename;
666 			while(*fn != '\0') {
667 				if(*fn == '/' || *fn == '\\') {
668 					char c = *fn;
669 					*fn = '\0';
670 #ifdef WIN32
671 					mkdir(filename);
672 #else
673 					mkdir(filename, 0700);
674 #endif
675 					*fn = c;
676 					do {
677 						fn++;
678 					} while(*fn == '/' || *fn == '\\');
679 				}
680 				else {
681 					fn++;
682 				}
683 			}
684 			// now try again to open the file...
685 			f_db = fopen(filename, "wb+");
686 			if(f_db == 0) {
687 				// we can't create the database!
688 				fprintf(stderr, "FATAL ERROR: can't open or create database file \"%s\" for package information.\n", filename);
689 				exit(1);
690 			}
691 		}
692 
693 // read the file at once [we need it this way to do binary searches]
694 // mmap would be a pain to manage since we would need to remap all the
695 // time whenever we insert new lines in the DB.
696 		fseek(f_db, 0, SEEK_END);
697 		f_db_size = ftell(f_db);
698 		fseek(f_db, 0, SEEK_SET);
699 
700 		if(f_db_size == 0) {
701 			// when the file just got created, print out a comment
702 			fprintf(f_db, "# Database of SSWF ActionScript Compiler (asc)\n");
703 			fprintf(f_db, "# DO NOT EDIT UNLESS YOU KNOW WHAT YOU ARE DOING\n");
704 			fprintf(f_db, "# Copyright (c) 2005-2009 by Made to Order Software Corp.\n");
705 			fprintf(f_db, "# WARNING: package names below MUST be sorted\n");
706 			fprintf(f_db, "# This file is written in UTF-8\n");
707 			fprintf(f_db, "# You can safely modify it with an editor which supports UTF-8\n");
708 			fprintf(f_db, "# package name + element name + type + file name + line number\n");
709 			fflush(f_db);
710 
711 			fseek(f_db, 0, SEEK_END);
712 			f_db_size = ftell(f_db);
713 			fseek(f_db, 0, SEEK_SET);
714 		}
715 
716 		delete [] f_db_data;
717 		f_db_data = new char[f_db_size + 2];
718 		if(fread(f_db_data, 1, f_db_size, f_db) != f_db_size) {
719 			fprintf(stderr, "FATAL ERROR: can't read the database file: \"%s\".\n", filename);
720 			exit(1);
721 		}
722 
723 		f_db_data[f_db_size] = '\0';
724 	}
725 
726 // "parse" the file and create the sorted array
727 // [the file is assumed to already be sorted]
728 	// count the lines which aren't commented out
729 	// and we remove empty lines and change new lines to just '\n'
730 	f_db_count = 0;
731 	char c, *s, *d;
732 	s = d = f_db_data;
733 	c = *s;
734 	while(c != '\0') {
735 		// skip leading spaces and empty lines
736 		// (those are lost)
737 		while(isspace(*s) || *s == '\n' || *s == '\r') {
738 			s++;
739 		}
740 		// don't count comments (we don't need them in our array)
741 		if(*s != '#') {
742 			f_db_count++;
743 		}
744 		while(*s != '\n' && *s != '\r' && *s != '\0') {
745 			*d++ = *s++;
746 		}
747 		// We need to skip this here otherwise a comment on
748 		// the last line generates an error!
749 		while(*s == '\n' || *s == '\r') {
750 			s++;
751 		}
752 		c = *s;
753 		// we only want '\n', no '\r\n' nor just '\r'
754 		*d++ = '\n';
755 	}
756 	*d = '\0';
757 	// f_db_size can now be smaller or 1 byte bigger
758 	f_db_size = d - f_db_data;
759 //fprintf(stderr, "Count = %ld\n", (long) f_db_count);
760 
761 	if(f_db_count < 1000) {
762 		f_db_max = 1000;
763 	}
764 	else {
765 		f_db_max = f_db_count + 100;
766 	}
767 
768 	// save the start of each line which isn't commented
769 	f_db_packages = new char *[f_db_max];
770 	s = f_db_data;
771 	char **p = f_db_packages;
772 	while(*s != '\0') {
773 		// ignore comments
774 		if(*s != '#') {
775 			*p++ = s;
776 		}
777 		// TODO: we really need to check the validity of each
778 		//	 entry and skip invalid lines as comments
779 		//	 go to the next line
780 		while(*s != '\0') {
781 			s++;
782 			if(s[-1] == '\n') {
783 				break;
784 			}
785 		}
786 	}
787 
788 // it's ready.
789 }
790 
791 
792 
793 
WriteDB(void)794 void IntCompiler::WriteDB(void)
795 {
796 	const char	*s, *start;
797 	size_t		idx;
798 
799 // if we are in ReadOnly mode, we simply cannot save the database
800 	if(f_db == 0) {
801 		return;
802 	}
803 
804 // clear the file
805 	fseek(f_db, 0, SEEK_SET);
806 #ifdef WIN32
807 	_chsize(fileno(f_db), 0);
808 #else
809 	ftruncate(fileno(f_db), 0);
810 #endif
811 
812 // save all the comments at the start of the file
813 	s = f_db_data;
814 	while(*s != '\0') {
815 		start = s;
816 		// ignore lines that are not comments
817 		if(*s != '#') {
818 			continue;
819 		}
820 		while(*s != '\n' && *s != '\0') {
821 			s++;
822 		}
823 		fprintf(f_db, "%.*s\n", s - start, start);
824 		while(*s == '\n') {
825 			s++;
826 		}
827 	}
828 
829 // now save the list of rows
830 	for(idx = 0; idx < f_db_count; ++idx) {
831 		start = s = f_db_packages[idx];
832 		while(*s != '\n' && *s != '\0') {
833 			s++;
834 		}
835 		fprintf(f_db, "%.*s\n", s - start, start);
836 	}
837 
838 // make sure it's written on disk
839 	fflush(f_db);
840 }
841 
842 
843 
844 // s2 ends with a space (name from database)
845 // s1 ends with a null terminator
pckcmp(const char * s1,const char * s2)846 int pckcmp(const char *s1, const char *s2)
847 {
848 	int	r, cnt;
849 
850 	cnt = 0;
851 	while(*s1 != '\0' && *s2 != '\n' && *s2 != '\0') {
852 		if(*s2 == ' ') {
853 			cnt++;
854 			if(cnt == 2) {
855 				break;
856 			}
857 			if(*s1 != ' ') {
858 				r = *s1 - *s2;
859 				return r < 0 ? -1 : 1;
860 			}
861 			s1++;
862 			s2++;
863 			if(s1[0] == '*' && s1[1] == '\0') {
864 				return 0;
865 			}
866 			continue;
867 		}
868 		r = *s1 - *s2;
869 		if(r != 0) {
870 			return r < 0 ? -1 : 1;
871 		}
872 		s1++;
873 		s2++;
874 	}
875 
876 	if(*s1 == '\0' && *s2 == ' ') {
877 		return 0;
878 	}
879 
880 	return *s1 != '\0' ? 1 : -1;
881 }
882 
883 
884 // Search for a named element:
885 // <package name>{.<package name>}.<class, function, variable name>
886 // TODO: add support for '*' in <package name>
FindElement(const String & package_name,const String & element_name,NodePtr * element,const char * type)887 const char *IntCompiler::FindElement(const String& package_name, const String& element_name, NodePtr *element, const char *type)
888 {
889 	int		i, j, p, r, len;
890 	size_t		sz;
891 	char		buf[16], **pck;
892 
893 	// transform the name in a UTF-8 string as supported by the database
894 	len = package_name.GetUTF8Length() + element_name.GetUTF8Length();
895 	if(len < 0) {
896 		fprintf(stderr, "INTERNAL ERROR: UTF8 convertion failed! (1)\n");
897 		exit(1);
898 	}
899 	len += 3;
900 #ifdef _MSVC
901 	// alloca() not available with cl
902 	class AutoDelete {
903 	public: AutoDelete(char *ptr) { f_ptr = ptr; }
904 		~AutoDelete() { delete f_ptr; }
905 	private: char *f_ptr;
906 	};
907 	char *n = new char[len];
908 	AutoDelete ad_n(n);
909 #else
910 	char n[len];
911 #endif
912 	sz = len;
913 	package_name.ToUTF8(n, sz);
914 	n[len - sz] = ' ';
915 	sz--;
916 	element_name.ToUTF8(n + len - sz, sz);
917 
918 	// the f_db_packages list of strings are sorted
919 	p = i = 0;
920 	j = f_db_count;
921 	if(j < 4) {
922 		// lack of precision forces us to do this with 0 to 3 elements
923 		p = 0;
924 		while(p < j) {
925 			r = pckcmp(n, f_db_packages[p]);
926 			if(r == 0) {
927 				goto found;
928 			}
929 			// when n < f_db_packages, we won't find anything
930 			if(r < 0) {
931 				break;
932 			}
933 			p++;
934 		}
935 	}
936 	else {
937 		// this is really the binary search
938 		// (see the / 2 below?)
939 		while(i < j) {
940 			p = i + (j - i) / 2;
941 			r = pckcmp(n, f_db_packages[p]);
942 			if(r == 0) {
943 				goto found;
944 			}
945 			if(r > 0) {
946 				p++;
947 				i = p;
948 			}
949 			else {
950 				j = p;
951 			}
952 		}
953 	}
954 
955 // we couldn't find this entry, when type != 0 insert a new row
956 	if(type == 0) {
957 		return 0;
958 	}
959 
960 	// we need to enlarge the f_db_packages buffer
961 	if(f_db_count >= f_db_max) {
962 		// enlarge buffer
963 		f_db_max += 250;
964 		pck = new char *[f_db_max];
965 		memcpy(pck, f_db_packages, sizeof(char *) * f_db_count);
966 		delete [] f_db_packages;
967 		f_db_packages = pck;
968 	}
969 
970 	if((int) f_db_count - p > 0) {
971 		memmove(f_db_packages + p + 1, f_db_packages + p, sizeof(char *) * (f_db_count - p));
972 	}
973 	f_db_count++;
974 
975 	// build the string which is:
976 	// <package name> <element name> <type> "<filename>" <line>
977 	{
978 	String entry(package_name);
979 	entry += " ";
980 	entry += element_name;
981 	entry += " ";
982 	entry += type;
983 	entry += " \"";
984 	entry += element->GetFilename();
985 	entry += "\" ";
986 	snprintf(buf, sizeof(buf), "%ld", element->GetLine());
987 	entry += buf;
988 	entry += "\n";
989 
990 	len = entry.GetUTF8Length();
991 	if(len < 0) {
992 		fprintf(stderr, "INTERNAL ERROR: UTF8 convertion failed! (2)\n");
993 		exit(1);
994 	}
995 	len += 2;
996 	f_db_packages[p] = new char[len];
997 	sz = len;
998 	if(entry.ToUTF8(f_db_packages[p], sz) < 0) {
999 		fprintf(stderr, "INTERNAL ERROR: UTF8 convertion failed! (3)\n");
1000 		exit(1);
1001 	}
1002 //fflush(stdout);
1003 //fprintf(stderr, "Add [%s]\n", f_db_packages[p]);
1004 	}
1005 
1006 	return f_db_packages[p];
1007 
1008 found:
1009 	// TODO: if type != 0 we want to test that the filename referenced
1010 	//	 is the same as the one in the database
1011 	return f_db_packages[p];
1012 }
1013 
1014 
FindPackages_AddDatabaseEntry(const String & package_name,NodePtr & element,const char * type)1015 void IntCompiler::FindPackages_AddDatabaseEntry(const String& package_name, NodePtr& element, const char *type)
1016 {
1017 	// here, we totally ignore internal, private
1018 	// and false entries right away
1019 	unsigned long attr = GetAttributes(element);
1020 	if((attr & (NODE_ATTR_PRIVATE | NODE_ATTR_FALSE | NODE_ATTR_INTERNAL)) != 0) {
1021 		return;
1022 	}
1023 
1024 	Data& data = element.GetData();
1025 	FindElement(package_name, data.f_str, &element, type);
1026 }
1027 
1028 
1029 
1030 // This function searches a list of directives for class, functions
1031 // and variables which are defined in a package. Their names are
1032 // then saved in the import database for fast search.
FindPackages_SavePackageElements(NodePtr & package,const String & package_name)1033 void IntCompiler::FindPackages_SavePackageElements(NodePtr& package, const String& package_name)
1034 {
1035 	int max = package.GetChildCount();
1036 	for(int idx = 0; idx < max; ++idx) {
1037 		NodePtr& child = package.GetChild(idx);
1038 		Data& data = child.GetData();
1039 		if(data.f_type == NODE_DIRECTIVE_LIST) {
1040 			FindPackages_SavePackageElements(child, package_name);
1041 		}
1042 		else if(data.f_type == NODE_CLASS) {
1043 			FindPackages_AddDatabaseEntry(
1044 					package_name,
1045 					child,
1046 					"class"
1047 				);
1048 		}
1049 		else if(data.f_type == NODE_FUNCTION) {
1050 			// we don't save prototypes, that's tested later
1051 			int flg = data.f_int.Get();
1052 			const char *type;
1053 			if((flg & NODE_FUNCTION_FLAG_GETTER) != 0) {
1054 				type = "getter";
1055 			}
1056 			else if((flg & NODE_FUNCTION_FLAG_SETTER) != 0) {
1057 				type = "setter";
1058 			}
1059 			else {
1060 				type = "function";
1061 			}
1062 			FindPackages_AddDatabaseEntry(
1063 					package_name,
1064 					child,
1065 					type
1066 				);
1067 		}
1068 		else if(data.f_type == NODE_VAR) {
1069 			int vcnt = child.GetChildCount();
1070 			for(int v = 0; v < vcnt; ++v) {
1071 				NodePtr& variable = child.GetChild(v);
1072 				// we don't save the variable type,
1073 				// it wouldn't help resolution
1074 				FindPackages_AddDatabaseEntry(
1075 						package_name,
1076 						variable,
1077 						"variable"
1078 					);
1079 			}
1080 		}
1081 		else if(data.f_type == NODE_PACKAGE) {
1082 			// sub packages
1083 			NodePtr& list = child.GetChild(0);
1084 			String name = package_name;
1085 			name += ".";
1086 			name += data.f_str;
1087 			FindPackages_SavePackageElements(list, name);
1088 		}
1089 	}
1090 }
1091 
1092 
1093 // this function searches the tree for packages (it stops at classes,
1094 // functions, and other such blocks)
FindPackages_DirectiveList(NodePtr & list)1095 void IntCompiler::FindPackages_DirectiveList(NodePtr& list)
1096 {
1097 	int max = list.GetChildCount();
1098 	for(int idx = 0; idx < max; ++idx) {
1099 		NodePtr& child = list.GetChild(idx);
1100 		Data& data = child.GetData();
1101 		if(data.f_type == NODE_DIRECTIVE_LIST) {
1102 			FindPackages_DirectiveList(child);
1103 		}
1104 		else if(data.f_type == NODE_PACKAGE) {
1105 			// Found a package! Save all the functions
1106 			// variables and classes in the database
1107 			// if not there yet.
1108 			NodePtr& directive_list = child.GetChild(0);
1109 			FindPackages_SavePackageElements(directive_list, data.f_str);
1110 		}
1111 	}
1112 }
1113 
1114 
FindPackages(NodePtr & program)1115 void IntCompiler::FindPackages(NodePtr& program)
1116 {
1117 	Data& data = program.GetData();
1118 	if(data.f_type != NODE_PROGRAM) {
1119 		return;
1120 	}
1121 
1122 	FindPackages_DirectiveList(program);
1123 }
1124 
1125 
LoadInternalPackages(const char * module)1126 void IntCompiler::LoadInternalPackages(const char *module)
1127 {
1128 #ifndef PATH_MAX
1129 #define	PATH_MAX 259
1130 #endif
1131 	char		buf[PATH_MAX], path[PATH_MAX];
1132 	size_t		sz;
1133 	DIR		*dir;
1134 
1135 	sz = sizeof(path);
1136 	g_rc.GetPath().ToUTF8(path, sz);
1137 	snprintf(buf, sizeof(buf), "%s/%s", path, module);
1138 	dir = opendir(buf);
1139 	if(dir == 0) {
1140 #ifdef WIN32
1141 		if(buf[0] != '/' && buf[0] != '\\'
1142 		&& (((buf[0] < 'a' || buf[0] > 'z')
1143 		  && (buf[0] < 'A' || buf[0] > 'Z'))
1144 				|| buf[1] != ':')) {
1145 			char fullpath[MAX_PATH * 5];
1146 
1147 			fullpath[0] = '\0';
1148 			fullpath[sizeof(fullpath) - 1] = '\0';
1149 			DWORD sz = GetModuleFileNameA(NULL, fullpath, sizeof(fullpath));
1150 			if(sz < MAX_PATH * 5) {
1151 				char *s = fullpath + strlen(fullpath);
1152 				while(s > fullpath) {
1153 					if(s[-1] == '\\') {
1154 						s--;
1155 						while(s > fullpath) {
1156 							if(s[-1] == '\\') {
1157 								strncpy(s, buf, (sizeof(fullpath) - 1) - (s - fullpath));
1158 								dir = opendir(fullpath);
1159 								break;
1160 							}
1161 							s--;
1162 						}
1163 						break;
1164 					}
1165 					s--;
1166 				}
1167 			}
1168 		}
1169 #endif
1170 		if(dir == 0) {
1171 			fprintf(stderr, "INSTALLATION ERROR: cannot read directory \"%s\".\n", buf);
1172 			exit(1);
1173 		}
1174 	}
1175 
1176 	struct dirent *ent;
1177 	while((ent = readdir(dir)) != 0) {
1178 		const char *s = ent->d_name;
1179 		const char *e = 0;
1180 		while(*s != '\0') {
1181 			if(*s == '.') {
1182 				e = s;
1183 			}
1184 			s++;
1185 		}
1186 		// TODO: under MS-Windows and other such file systems,
1187 		//	 a filename is not case sensitive
1188 		if(e == 0 || strcmp(e, ".asc") != 0
1189 		|| strcmp(ent->d_name, "as_init.asc") == 0) {
1190 			continue;
1191 		}
1192 		// we've got a file of interest
1193 		// TODO: we want to keep this package in RAM since
1194 		//	 we already parsed it!
1195 		NodePtr p = LoadModule(module, ent->d_name);
1196 		// now we can search the package in the actual code
1197 		FindPackages(p);
1198 	}
1199 
1200 	// avoid leaks
1201 	closedir(dir);
1202 }
1203 
1204 
InternalImports(void)1205 void IntCompiler::InternalImports(void)
1206 {
1207 	if(!g_global_import.HasNode()) {
1208 #if defined(_DEBUG) || defined(DEBUG)
1209 fflush(stdout);
1210 #endif
1211 		// Read the resource file
1212 		g_rc.FindRC(f_home, f_input_retriever != NULL);
1213 		g_rc.ReadRC();
1214 		g_rc.Close();
1215 
1216 		g_global_import = LoadModule("global", "as_init.asc");
1217 		g_system_import = LoadModule("system", "as_init.asc");
1218 		g_native_import = LoadModule("native", "as_init.asc");
1219 	}
1220 
1221 	ReadDB();
1222 
1223 	if(f_db_count == 0) {
1224 		LoadInternalPackages("global");
1225 		LoadInternalPackages("system");
1226 		LoadInternalPackages("native");
1227 
1228 		// this saves the internal packages info
1229 		WriteDB();
1230 	}
1231 }
1232 
1233 
1234 
1235 };	// namespace as
1236 };	// namespace sswf
1237