/* $Id$ */ /* Copyright (c) 2008-2013 Pierre Pronchery */ /* This file is part of DeforaOS Devel cpp */ /* This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, version 3 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ /* FIXME: * - potential memory leak with tokens' data * - apparently not checking that scopes are properly closed upon exit */ #include #include #include #include #include #include #include #include "common.h" #ifdef DEBUG # define DEBUG_SCOPE() fprintf(stderr, "DEBUG: %s(%p) %zu scope=%d\n", \ __func__, cpp, _cpp_scope_get_count(cpp), _cpp_scope_get(cpp)) #else # define DEBUG_SCOPE() #endif /* private */ /* prototypes */ /* scope */ static int _cpp_scope_push(Cpp * cpp, CppScope scope); static CppScope _cpp_scope_get(Cpp * cpp); static size_t _cpp_scope_get_count(Cpp * cpp); static void _cpp_scope_set(Cpp * cpp, CppScope scope); static int _cpp_scope_pop(Cpp * cpp); /* functions */ /* scope */ /* cpp_scope_push */ static int _cpp_scope_push(Cpp * cpp, CppScope scope) { CppScope * p; if(_cpp_scope_get(cpp) != CPP_SCOPE_TAKING) scope = CPP_SCOPE_TAKEN; if((p = realloc(cpp->scopes, sizeof(*p) * (cpp->scopes_cnt + 1))) == NULL) return error_set_code(1, "%s", strerror(errno)); cpp->scopes = p; p[cpp->scopes_cnt++] = scope; return 0; } /* cpp_scope_get */ static CppScope _cpp_scope_get(Cpp * cpp) { return (cpp->scopes_cnt == 0) ? CPP_SCOPE_TAKING : cpp->scopes[cpp->scopes_cnt - 1]; } /* cpp_scope_get_count */ static size_t _cpp_scope_get_count(Cpp * cpp) { return cpp->scopes_cnt; } /* cpp_scope_set */ static void _cpp_scope_set(Cpp * cpp, CppScope scope) { assert(cpp->scopes_cnt > 0); cpp->scopes[cpp->scopes_cnt - 1] = scope; } /* cpp_scope_pop */ static int _cpp_scope_pop(Cpp * cpp) { CppScope * p; assert(cpp->scopes_cnt > 0); if(cpp->scopes_cnt == 1) { free(cpp->scopes); p = NULL; } else if((p = realloc(cpp->scopes, sizeof(*p) * (cpp->scopes_cnt - 1))) == NULL) return error_set_code(1, "%s", strerror(errno)); cpp->scopes = p; cpp->scopes_cnt--; return 0; } /* public */ /* cpp_scan */ static void _scan_meta_line(Token * token); static int _scan_ifdef(Cpp * cpp, Token ** token); static int _scan_ifndef(Cpp * cpp, Token ** token); static int _scan_if(Cpp * cpp, Token ** token); static CppScope _if_do(Cpp * cpp, char const * str, int level); static int _scan_elif(Cpp * cpp, Token ** token); static int _scan_else(Cpp * cpp, Token ** token); static int _scan_endif(Cpp * cpp, Token ** token); static int _scan_define(Cpp * cpp, Token * token); static int _scan_include(Cpp * cpp, Token * token); static int _scan_undef(Cpp * cpp, Token * token); int cpp_scan(Cpp * cpp, Token ** token) { int ret; TokenCode code; char const * str; char const * s; for(; (ret = cppparser_scan(cpp->parser, token)) == 0; token_delete(*token)) { if(*token == NULL) /* end of file */ break; if((code = token_get_code(*token)) >= CPP_CODE_META_FIRST && code <= CPP_CODE_META_LAST) _scan_meta_line(*token); if(cpp->options & CPP_OPTION_IGNORE_META) { if(code == CPP_CODE_META_ERROR || code == CPP_CODE_META_WARNING) continue; return 0; } switch(code) { case CPP_CODE_META_IFDEF: return _scan_ifdef(cpp, token); case CPP_CODE_META_IFNDEF: return _scan_ifndef(cpp, token); case CPP_CODE_META_IF: return _scan_if(cpp, token); case CPP_CODE_META_ELIF: return _scan_elif(cpp, token); case CPP_CODE_META_ELSE: return _scan_else(cpp, token); case CPP_CODE_META_ENDIF: return _scan_endif(cpp, token); default: break; } if(_cpp_scope_get(cpp) != CPP_SCOPE_TAKING) /* not in scope */ continue; switch(code) { case CPP_CODE_META_DEFINE: return _scan_define(cpp, *token); case CPP_CODE_META_INCLUDE: return _scan_include(cpp, *token); case CPP_CODE_META_UNDEF: return _scan_undef(cpp, *token); case CPP_CODE_WORD: str = token_get_string(*token); if((s = cpp_define_get(cpp, str)) != NULL) { if(strcmp(str, s) == 0) /* XXX hack */ return 0; if(cppparser_inject(cpp->parser, s) != 0) return -1; continue; } return 0; default: return 0; } } return ret; } static void _scan_meta_line(Token * token) { /* decrement the line number, not so elegant but works */ token_set_line(token, token_get_line(token) - 1); } static int _scan_ifdef(Cpp * cpp, Token ** token) { char * name; int take; DEBUG_SCOPE(); name = token_get_data(*token); take = (cpp_define_get(cpp, name) != NULL) ? 1 : 0; token_set_data(*token, NULL); free(name); _cpp_scope_push(cpp, take ? CPP_SCOPE_TAKING : CPP_SCOPE_NOTYET); return 0; } static int _scan_ifndef(Cpp * cpp, Token ** token) { char * name; int take; DEBUG_SCOPE(); name = token_get_data(*token); take = (cpp_define_get(cpp, name) == NULL) ? 1 : 0; token_set_data(*token, NULL); free(name); _cpp_scope_push(cpp, take ? CPP_SCOPE_TAKING : CPP_SCOPE_NOTYET); return 0; } static int _scan_if(Cpp * cpp, Token ** token) { char * str; DEBUG_SCOPE(); str = token_get_data(*token); _cpp_scope_push(cpp, _if_do(cpp, str, 0)); token_set_data(*token, NULL); free(str); return 0; } static CppScope _if_do(Cpp * cpp, char const * str, int level) { char * p; char const * q; long l; if(str == NULL || level >= 10) /* FIXME it's probably an error case instead */ return CPP_SCOPE_NOTYET; if(str[0] == '!') return (_if_do(cpp, &str[1], level) == CPP_SCOPE_TAKING) ? CPP_SCOPE_NOTYET : CPP_SCOPE_TAKING; l = strtol(str, &p, 0); if(str[0] != '\0' && p != str) return (l != 0) ? CPP_SCOPE_TAKING : CPP_SCOPE_NOTYET; if(strncmp(str, "defined(", 8) == 0 && (p = strchr(str, ')')) != NULL) { *p = '\0'; return (cpp_define_get(cpp, &str[8]) != NULL) ? CPP_SCOPE_TAKING : CPP_SCOPE_NOTYET; } else if((q = cpp_define_get(cpp, str)) != NULL) return _if_do(cpp, q, level + 1); /* FIXME really check the condition */ return CPP_SCOPE_NOTYET; } static int _scan_elif(Cpp * cpp, Token ** token) { int ret = 0; CppScope scope; char * str; DEBUG_SCOPE(); str = token_get_data(*token); if(_cpp_scope_get_count(cpp) == 0) { token_set_code(*token, CPP_CODE_META_ERROR); token_set_string(*token, "#elif without #if or #ifdef" " or #ifndef"); ret = 0; } else { scope = _cpp_scope_get(cpp); if(scope == CPP_SCOPE_TAKING) _cpp_scope_set(cpp, CPP_SCOPE_TAKEN); else if(scope == CPP_SCOPE_NOTYET) _cpp_scope_set(cpp, _if_do(cpp, str, 0)); } if(str != NULL) { token_set_data(*token, NULL); free(str); } return ret; } static int _scan_else(Cpp * cpp, Token ** token) { CppScope scope; DEBUG_SCOPE(); if(_cpp_scope_get_count(cpp) == 0) { token_set_code(*token, CPP_CODE_META_ERROR); token_set_string(*token, "#else without #if or #ifdef" " or #ifndef"); return 0; } scope = _cpp_scope_get(cpp); if(scope == CPP_SCOPE_TAKING) _cpp_scope_set(cpp, CPP_SCOPE_TAKEN); else if(scope == CPP_SCOPE_NOTYET) _cpp_scope_set(cpp, CPP_SCOPE_TAKING); return 0; } static int _scan_endif(Cpp * cpp, Token ** token) { DEBUG_SCOPE(); if(_cpp_scope_get_count(cpp) == 0) { token_set_code(*token, CPP_CODE_META_ERROR); token_set_string(*token, "#endif without #if or #ifdef" " or #ifndef"); } _cpp_scope_pop(cpp); return 0; } static int _scan_define(Cpp * cpp, Token * token) { char * str; int tmp; size_t i; size_t j; size_t k = 0; char * var; char const * val; str = token_get_data(token); /* fetch variable name */ for(i = 1; (tmp = str[i]) != '\0' && !isspace(tmp); i++) { /* FIXME actually implement macros */ if(str[i] != '(') continue; for(k = i; str[i] != '\0' && str[i] != ')'; i++); if(str[i] == ')') i++; break; } /* skip white-spaces and fetch value */ for(j = i; (tmp = str[j]) != '\0' && isspace(tmp); j++); val = (str[j] != '\0') ? &str[j] : NULL; if((var = strdup(str)) == NULL) { token_set_code(token, CPP_CODE_META_ERROR); token_set_string(token, strerror(errno)); token_set_data(token, NULL); free(str); return 0; } var[k != 0 ? k : i] = '\0'; if(cpp_define_add(cpp, var, val) != 0) { token_set_code(token, CPP_CODE_META_ERROR); token_set_string(token, error_get()); } free(var); token_set_data(token, NULL); free(str); return 0; } static int _scan_include(Cpp * cpp, Token * token) { if(cppparser_include(cpp->parser, token_get_data(token)) == 0) return 0; token_set_code(token, CPP_CODE_META_ERROR); token_set_string(token, error_get()); return 0; } static int _scan_undef(Cpp * cpp, Token * token) /* FIXME ignores what's after the spaces after the variable name */ { char * str; int tmp; size_t i; char * var; str = token_get_data(token); /* fetch variable name */ for(i = 1; (tmp = str[i]) != '\0' && !isspace(tmp); i++); if((var = strdup(str)) == NULL) { token_set_code(token, CPP_CODE_META_ERROR); token_set_string(token, strerror(errno)); free(str); return 0; } var[i] = '\0'; if(cpp_define_remove(cpp, var) != 0) { token_set_code(token, CPP_CODE_META_ERROR); token_set_string(token, error_get()); } free(var); free(str); return 0; }