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 38char lexfile[B_PATH_NAME_LENGTH]; 39 40static BEntry entry; 41static BFile file; 42BResources rsrc; 43const char* rsrc_file; 44 45static 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 56struct mem_t { 57 void* ptr; 58 char* file; 59 int32 line; 60}; 61 62typedef std::list<mem_t> mem_list_t; 63typedef mem_list_t::iterator mem_iter_t; 64 65static mem_list_t mem_list; 66#else 67static ptr_list_t mem_list; 68#endif 69 70 71class AddIncludeDir { 72 public: 73 AddIncludeDir(const char *file); 74 ~AddIncludeDir(); 75 76 private: 77 BPath fPath; 78}; 79 80 81AddIncludeDir::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 97AddIncludeDir::~AddIncludeDir() 98{ 99 if (fPath.InitCheck() == B_OK) 100 rdef_remove_include_dir(fPath.Path()); 101} 102 103 104// #pragma mark - 105 106 107void * 108alloc_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 128void 129free_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 149static void 150clean_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 168void 169abort_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 185void 186abort_compile() 187{ 188 longjmp(abort_jmp, 1); 189} 190 191 192static void 193compile_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 228static status_t 229open_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 250static void 251close_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 263status_t 264rdef_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