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