xref: /haiku/src/bin/rc/compile.cpp (revision 1e60bdeab63fa7a57bc9a55b032052e95a18bd2c)
1 /*
2  * Copyright (c) 2003 Matthijs Hollemans
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20  * DEALINGS IN THE SOFTWARE.
21  */
22 
23 #include <Entry.h>
24 #include <File.h>
25 #include <Path.h>
26 
27 #include <setjmp.h>
28 #include <stdarg.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 
33 #include "rdef.h"
34 #include "compile.h"
35 #include "private.h"
36 #include "parser.hpp"
37 
38 char lexfile[B_PATH_NAME_LENGTH];
39 
40 static BEntry entry;
41 static BFile file;
42 BResources rsrc;
43 const char* rsrc_file;
44 
45 static jmp_buf abort_jmp;  // for aborting compilation
46 
47 // When it encounters an error, the parser immediately aborts (we don't try
48 // error recovery). But its parse stack may still contain malloc'ed objects.
49 // We keep track of these memory blocks in the mem_list, so we can properly
50 // free them in case of an error (which is the polite thing to do since we
51 // are a shared library). If the compilation was successful, then mem_list
52 // should be empty. In DEBUG mode we dump some statistics to verify that the
53 // parser properly frees up objects when it is done with them.
54 
55 #ifdef DEBUG
56 struct mem_t {
57 	void* ptr;
58 	char* file;
59 	int32 line;
60 };
61 
62 typedef std::list<mem_t> mem_list_t;
63 typedef mem_list_t::iterator mem_iter_t;
64 
65 static mem_list_t mem_list;
66 #else
67 static ptr_list_t mem_list;
68 #endif
69 
70 
71 class AddIncludeDir {
72 	public:
73 		AddIncludeDir(const char *file);
74 		~AddIncludeDir();
75 
76 	private:
77 		BPath fPath;
78 };
79 
80 
81 AddIncludeDir::AddIncludeDir(const char *file)
82 {
83 	// ignore the special stdin file
84 	if (!strcmp(file, "-"))
85 		return;
86 
87 	if (fPath.SetTo(file) != B_OK
88 		|| fPath.GetParent(&fPath) != B_OK) {
89 		fPath.Unset();
90 		return;
91 	}
92 
93 	rdef_add_include_dir(fPath.Path(), false);
94 }
95 
96 
97 AddIncludeDir::~AddIncludeDir()
98 {
99 	if (fPath.InitCheck() == B_OK)
100 		rdef_remove_include_dir(fPath.Path());
101 }
102 
103 
104 //	#pragma mark -
105 
106 
107 void *
108 alloc_mem(size_t size)
109 {
110 	void *ptr = malloc(size);  // can be 0
111 	if (ptr == NULL)
112 		abort_compile(B_NO_MEMORY, "out of memory");
113 
114 #ifdef DEBUG
115 	mem_t mem;
116 	mem.ptr = ptr;
117 	mem.file = strdup(lexfile);
118 	mem.line = yylineno;
119 	mem_list.push_front(mem);
120 #else
121 	mem_list.push_front(ptr);
122 #endif
123 
124 	return ptr;
125 }
126 
127 
128 void
129 free_mem(void *ptr)
130 {
131 	if (ptr != NULL) {
132 #ifdef DEBUG
133 		for (mem_iter_t i = mem_list.begin(); i != mem_list.end(); ++i) {
134 			if (i->ptr == ptr) {
135 				free(i->ptr);
136 				free(i->file);
137 				mem_list.erase(i);
138 				return;
139 			}
140 		}
141 #else
142 		mem_list.remove(ptr);
143 		free(ptr);
144 #endif
145 	}
146 }
147 
148 
149 static void
150 clean_up_mem()
151 {
152 #ifdef DEBUG
153 	if (mem_list.size() != 0)
154 		printf("mem_list leaks %ld objects\n", mem_list.size());
155 
156 	for (mem_iter_t i = mem_list.begin(); i != mem_list.end(); ) {
157 		printf("%p allocated at %s:%ld\n", i->ptr, i->file, i->line);
158 		free(i->ptr);
159 		free(i->file);
160 		i = mem_list.erase(i);
161 	}
162 #else
163 	free_ptr_list(mem_list);
164 #endif
165 }
166 
167 
168 void
169 abort_compile(status_t err, const char *format, ...)
170 {
171 	va_list ap;
172 
173 	rdef_err = err;
174 	rdef_err_line = yylineno;
175 	strcpy(rdef_err_file, lexfile);
176 
177 	va_start(ap, format);
178 	vsprintf(rdef_err_msg, format, ap);
179 	va_end(ap);
180 
181 	abort_compile();
182 }
183 
184 
185 void
186 abort_compile()
187 {
188 	longjmp(abort_jmp, 1);
189 }
190 
191 
192 static void
193 compile_file(char *file)
194 {
195 	strcpy(lexfile, file);
196 
197 	// "-" means reading from stdin
198 	if (strcmp(file, "-"))
199 		yyin = fopen(lexfile, "r");
200 	else
201 		yyin = stdin;
202 
203 	if (yyin == NULL) {
204 		strcpy(rdef_err_file, lexfile);
205 		rdef_err = RDEF_FILE_NOT_FOUND;
206 		return;
207 	}
208 
209 	init_lexer();
210 	init_parser();
211 
212 	if (setjmp(abort_jmp) == 0) {
213 		yyparse();
214 	}
215 
216 	// About error handling: If the bison-generated parser encounters
217 	// a syntax error, it calls yyerror(), aborts parsing, and returns
218 	// from yyparse(). For other kinds of errors (semantics, problem
219 	// writing to BResources, etc), we bail out with a longjmp(). From
220 	// then on, we can tell success or failure by looking at rdef_err.
221 
222 	clean_up_lexer();
223 	clean_up_parser();
224 	clean_up_mem();
225 }
226 
227 
228 static status_t
229 open_output_file()
230 {
231 	status_t err = entry.SetTo(rsrc_file, true);
232 	if (err == B_OK) {
233 		uint32 openMode = B_READ_WRITE | B_CREATE_FILE;
234 		bool clobber = false;
235 
236 		if (!(flags & RDEF_MERGE_RESOURCES)) {
237 			openMode |= B_ERASE_FILE;
238 			clobber   = true;
239 		}
240 
241 		err = file.SetTo(&entry, openMode);
242 		if (err == B_OK)
243 			err = rsrc.SetTo(&file, clobber);
244 	}
245 
246 	return err;
247 }
248 
249 
250 static void
251 close_output_file()
252 {
253 	if (rdef_err == B_OK || (flags & RDEF_MERGE_RESOURCES) != 0)
254 		rsrc.Sync();
255 	else
256 		entry.Remove();  // throw away output file
257 
258 	file.Unset();
259 	entry.Unset();
260 }
261 
262 
263 status_t
264 rdef_compile(const char *outputFile)
265 {
266 	clear_error();
267 
268 	if (outputFile == NULL || outputFile[0] == '\0') {
269 		rdef_err = B_BAD_VALUE;
270 		return rdef_err;
271 	}
272 
273 	rsrc_file = outputFile;
274 	rdef_err = open_output_file();
275 	if (rdef_err != B_OK)
276 		return rdef_err;
277 
278 	for (ptr_iter_t i = input_files.begin();
279 			(i != input_files.end()) && (rdef_err == B_OK); ++i) {
280 		char *path = (char *)*i;
281 
282 		AddIncludeDir add(path);
283 		compile_file(path);
284 	}
285 
286 	close_output_file();
287 	return rdef_err;
288 }
289 
290