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
AddIncludeDir(const char * file)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
~AddIncludeDir()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 *
alloc_mem(size_t size)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
free_mem(void * ptr)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
clean_up_mem()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
abort_compile(status_t err,const char * format,...)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
abort_compile()186 abort_compile()
187 {
188 longjmp(abort_jmp, 1);
189 }
190
191
192 static void
compile_file(char * file)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
open_output_file()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
close_output_file()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
rdef_compile(const char * outputFile)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