/*
* Copyright (c) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2008, 2009,
* 2010
* Tama Communications Corporation
*
* This file is part of GNU GLOBAL.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#ifdef HAVE_CONFIG_H
#include
#endif
#ifdef HAVE_STRING_H
#include
#else
#include
#endif
#include "internal.h"
#include "die.h"
#include "strbuf.h"
#include "strlimcpy.h"
#include "token.h"
#include "cpp_res.h"
static void process_attribute(const struct parser_param *);
static int function_definition(const struct parser_param *);
static void condition_macro(const struct parser_param *, int);
static int enumerator_list(const struct parser_param *);
/** max size of complete name of class */
#define MAXCOMPLETENAME 1024
/** max size of class stack */
#define MAXCLASSSTACK 100
#define IS_CV_QUALIFIER(c) ((c) == CPP_CONST || (c) == CPP_VOLATILE)
#define MAXPIFSTACK 100
/**
* @name #ifdef stack.
*/
static struct {
short start; /**< level when @CODE{\#if} block started */
short end; /**< level when @CODE{\#if} block end */
short if0only; /**< @CODE{\#if 0} or notdef only */
} pifstack[MAXPIFSTACK], *cur;
static int piflevel; /**< condition macro level */
static int level; /**< brace level */
static int namespacelevel; /**< namespace block level */
/**
* Cpp: read C++ file and pickup tag entries.
*/
void
Cpp(const struct parser_param *param)
{
int c, cc;
int savelevel;
int startclass, startthrow, startmacro, startsharp, startequal;
char classname[MAXTOKEN];
char completename[MAXCOMPLETENAME];
char *completename_limit = &completename[sizeof(completename)];
int classlevel;
struct {
char *classname;
char *terminate;
int level;
} stack[MAXCLASSSTACK];
const char *interested = "{}=;~";
STRBUF *sb = strbuf_open(0);
*classname = *completename = 0;
stack[0].classname = completename;
stack[0].terminate = completename;
stack[0].level = 0;
level = classlevel = piflevel = namespacelevel = 0;
savelevel = -1;
startclass = startthrow = startmacro = startsharp = startequal = 0;
if (!opentoken(param->file))
die("'%s' cannot open.", param->file);
cmode = 1; /* allow token like '#xxx' */
crflag = 1; /* require '\n' as a token */
cppmode = 1; /* treat '::' as a token */
while ((cc = nexttoken(interested, cpp_reserved_word)) != EOF) {
if (cc == '~' && level == stack[classlevel].level)
continue;
switch (cc) {
case SYMBOL: /* symbol */
if (startclass || startthrow) {
PUT(PARSER_REF_SYM, token, lineno, sp);
} else if (peekc(0) == '('/* ) */) {
if (param->isnotfunction(token)) {
PUT(PARSER_REF_SYM, token, lineno, sp);
} else if (level > stack[classlevel].level || startequal || startmacro) {
PUT(PARSER_REF_SYM, token, lineno, sp);
} else if (level == stack[classlevel].level && !startmacro && !startsharp && !startequal) {
char savetok[MAXTOKEN], *saveline;
int savelineno = lineno;
strlimcpy(savetok, token, sizeof(savetok));
strbuf_reset(sb);
strbuf_puts(sb, sp);
saveline = strbuf_value(sb);
if (function_definition(param)) {
/* ignore constructor */
if (strcmp(stack[classlevel].classname, savetok))
PUT(PARSER_DEF, savetok, savelineno, saveline);
} else {
PUT(PARSER_REF_SYM, savetok, savelineno, saveline);
}
}
} else {
PUT(PARSER_REF_SYM, token, lineno, sp);
}
break;
case CPP_USING:
/*
* using namespace name;
* using ...;
*/
if ((c = nexttoken(interested, cpp_reserved_word)) == CPP_NAMESPACE) {
if ((c = nexttoken(interested, cpp_reserved_word)) == SYMBOL) {
PUT(PARSER_REF_SYM, token, lineno, sp);
} else {
if (param->flags & PARSER_WARNING)
warning("missing namespace name. [+%d %s].", lineno, curfile);
pushbacktoken();
}
} else
pushbacktoken();
break;
case CPP_NAMESPACE:
crflag = 0;
/*
* namespace name = ...;
* namespace [name] { ... }
*/
if ((c = nexttoken(interested, cpp_reserved_word)) == SYMBOL) {
PUT(PARSER_DEF, token, lineno, sp);
if ((c = nexttoken(interested, cpp_reserved_word)) == '=') {
crflag = 1;
break;
}
}
/*
* Namespace block doesn't have any influence on level.
*/
if (c == '{') /* } */ {
namespacelevel++;
} else {
if (param->flags & PARSER_WARNING)
warning("missing namespace block. [+%d %s](0x%x).", lineno, curfile, c);
}
crflag = 1;
break;
case CPP_EXTERN: /* for 'extern "C"/"C++"' */
if (peekc(0) != '"') /* " */
continue; /* If does not start with '"', continue. */
while ((c = nexttoken(interested, cpp_reserved_word)) == '\n')
;
/*
* 'extern "C"/"C++"' block is a kind of namespace block.
* (It doesn't have any influence on level.)
*/
if (c == '{') /* } */
namespacelevel++;
else
pushbacktoken();
break;
case CPP_CLASS:
DBG_PRINT(level, "class");
if ((c = nexttoken(interested, cpp_reserved_word)) == SYMBOL) {
strlimcpy(classname, token, sizeof(classname));
/*
* Ignore forward definitions.
* "class name;"
*/
if (peekc(0) != ';') {
startclass = 1;
PUT(PARSER_DEF, token, lineno, sp);
}
}
break;
case '{': /* } */
DBG_PRINT(level, "{"); /* } */
++level;
if ((param->flags & PARSER_BEGIN_BLOCK) && atfirst) {
if ((param->flags & PARSER_WARNING) && level != 1)
warning("forced level 1 block start by '{' at column 0 [+%d %s].", lineno, curfile); /* } */
level = 1;
}
if (startclass) {
char *p = stack[classlevel].terminate;
char *q = classname;
if (++classlevel >= MAXCLASSSTACK)
die("class stack over flow.[%s]", curfile);
if (classlevel > 1 && p < completename_limit)
*p++ = '.';
stack[classlevel].classname = p;
while (*q && p < completename_limit)
*p++ = *q++;
stack[classlevel].terminate = p;
stack[classlevel].level = level;
*p++ = 0;
}
startclass = startthrow = 0;
break;
/* { */
case '}':
if (--level < 0) {
if (namespacelevel > 0)
namespacelevel--;
else if (param->flags & PARSER_WARNING)
warning("missing left '{' [+%d %s].", lineno, curfile); /* } */
level = 0;
}
if ((param->flags & PARSER_END_BLOCK) && atfirst) {
if ((param->flags & PARSER_WARNING) && level != 0)
/* { */
warning("forced level 0 block end by '}' at column 0 [+%d %s].", lineno, curfile);
level = 0;
}
if (level < stack[classlevel].level)
*(stack[--classlevel].terminate) = 0;
/* { */
DBG_PRINT(level, "}");
break;
case '=':
/* dirty hack. Don't mimic this. */
if (peekc(0) == '=') {
throwaway_nextchar();
} else {
startequal = 1;
}
break;
case ';':
startthrow = startequal = 0;
break;
case '\n':
if (startmacro && level != savelevel) {
if (param->flags & PARSER_WARNING)
warning("different level before and after #define macro. reseted. [+%d %s].", lineno, curfile);
level = savelevel;
}
startmacro = startsharp = 0;
break;
/*
* #xxx
*/
case SHARP_DEFINE:
case SHARP_UNDEF:
startmacro = 1;
savelevel = level;
if ((c = nexttoken(interested, cpp_reserved_word)) != SYMBOL) {
pushbacktoken();
break;
}
if (peekc(1) == '('/* ) */) {
PUT(PARSER_DEF, token, lineno, sp);
while ((c = nexttoken("()", cpp_reserved_word)) != EOF && c != '\n' && c != /* ( */ ')')
if (c == SYMBOL)
PUT(PARSER_REF_SYM, token, lineno, sp);
if (c == '\n')
pushbacktoken();
} else {
PUT(PARSER_DEF, token, lineno, sp);
}
break;
case SHARP_IMPORT:
case SHARP_INCLUDE:
case SHARP_INCLUDE_NEXT:
case SHARP_ERROR:
case SHARP_LINE:
case SHARP_PRAGMA:
case SHARP_WARNING:
case SHARP_IDENT:
case SHARP_SCCS:
while ((c = nexttoken(interested, cpp_reserved_word)) != EOF && c != '\n')
;
break;
case SHARP_IFDEF:
case SHARP_IFNDEF:
case SHARP_IF:
case SHARP_ELIF:
case SHARP_ELSE:
case SHARP_ENDIF:
condition_macro(param, cc);
break;
case SHARP_SHARP: /* ## */
(void)nexttoken(interested, cpp_reserved_word);
break;
case CPP_NEW:
if ((c = nexttoken(interested, cpp_reserved_word)) == SYMBOL)
PUT(PARSER_REF_SYM, token, lineno, sp);
break;
case CPP_STRUCT:
case CPP_ENUM:
case CPP_UNION:
while ((c = nexttoken(interested, cpp_reserved_word)) == CPP___ATTRIBUTE__)
process_attribute(param);
if (c == SYMBOL) {
if (peekc(0) == '{') /* } */ {
PUT(PARSER_DEF, token, lineno, sp);
} else {
PUT(PARSER_REF_SYM, token, lineno, sp);
}
c = nexttoken(interested, cpp_reserved_word);
}
if (c == '{' /* } */ && cc == CPP_ENUM) {
enumerator_list(param);
} else {
pushbacktoken();
}
break;
case CPP_TEMPLATE:
{
int level = 0;
while ((c = nexttoken("<>", cpp_reserved_word)) != EOF) {
if (c == '<')
++level;
else if (c == '>') {
if (--level == 0)
break;
} else if (c == SYMBOL) {
PUT(PARSER_REF_SYM, token, lineno, sp);
}
}
if (c == EOF && (param->flags & PARSER_WARNING))
warning("template <...> isn't closed. [+%d %s].", lineno, curfile);
}
break;
case CPP_OPERATOR:
while ((c = nexttoken(";{", /* } */ cpp_reserved_word)) != EOF) {
if (c == '{') /* } */ {
pushbacktoken();
break;
} else if (c == ';') {
break;
} else if (c == SYMBOL) {
PUT(PARSER_REF_SYM, token, lineno, sp);
}
}
if (c == EOF && (param->flags & PARSER_WARNING))
warning("'{' doesn't exist after 'operator'. [+%d %s].", lineno, curfile); /* } */
break;
/* control statement check */
case CPP_THROW:
startthrow = 1;
case CPP_BREAK:
case CPP_CASE:
case CPP_CATCH:
case CPP_CONTINUE:
case CPP_DEFAULT:
case CPP_DELETE:
case CPP_DO:
case CPP_ELSE:
case CPP_FOR:
case CPP_GOTO:
case CPP_IF:
case CPP_RETURN:
case CPP_SWITCH:
case CPP_TRY:
case CPP_WHILE:
if ((param->flags & PARSER_WARNING) && !startmacro && level == 0)
warning("Out of function. %8s [+%d %s]", token, lineno, curfile);
break;
case CPP_TYPEDEF:
{
/*
* This parser is too complex to maintain.
* We should rewrite the whole.
*/
char savetok[MAXTOKEN];
int savelineno = 0;
int typedef_savelevel = level;
savetok[0] = 0;
/* skip CV qualifiers */
do {
c = nexttoken("{}(),;", cpp_reserved_word);
} while (IS_CV_QUALIFIER(c) || c == '\n');
if ((param->flags & PARSER_WARNING) && c == EOF) {
warning("unexpected eof. [+%d %s]", lineno, curfile);
break;
} else if (c == CPP_ENUM || c == CPP_STRUCT || c == CPP_UNION) {
char *interest_enum = "{},;";
int c_ = c;
while ((c = nexttoken(interest_enum, cpp_reserved_word)) == CPP___ATTRIBUTE__)
process_attribute(param);
/* read tag name if exist */
if (c == SYMBOL) {
if (peekc(0) == '{') /* } */ {
PUT(PARSER_DEF, token, lineno, sp);
} else {
PUT(PARSER_REF_SYM, token, lineno, sp);
}
c = nexttoken(interest_enum, cpp_reserved_word);
}
if (c_ == CPP_ENUM) {
if (c == '{') /* } */
c = enumerator_list(param);
else
pushbacktoken();
} else {
for (; c != EOF; c = nexttoken(interest_enum, cpp_reserved_word)) {
switch (c) {
case SHARP_IFDEF:
case SHARP_IFNDEF:
case SHARP_IF:
case SHARP_ELIF:
case SHARP_ELSE:
case SHARP_ENDIF:
condition_macro(param, c);
continue;
default:
break;
}
if (c == ';' && level == typedef_savelevel) {
if (savetok[0])
PUT(PARSER_DEF, savetok, savelineno, sp);
break;
} else if (c == '{')
level++;
else if (c == '}') {
if (--level == typedef_savelevel)
break;
} else if (c == SYMBOL) {
PUT(PARSER_REF_SYM, token, lineno, sp);
/* save lastest token */
strlimcpy(savetok, token, sizeof(savetok));
savelineno = lineno;
}
}
if (c == ';')
break;
}
if ((param->flags & PARSER_WARNING) && c == EOF) {
warning("unexpected eof. [+%d %s]", lineno, curfile);
break;
}
} else if (c == SYMBOL) {
PUT(PARSER_REF_SYM, token, lineno, sp);
}
savetok[0] = 0;
while ((c = nexttoken("(),;", cpp_reserved_word)) != EOF) {
switch (c) {
case SHARP_IFDEF:
case SHARP_IFNDEF:
case SHARP_IF:
case SHARP_ELIF:
case SHARP_ELSE:
case SHARP_ENDIF:
condition_macro(param, c);
continue;
default:
break;
}
if (c == '(')
level++;
else if (c == ')')
level--;
else if (c == SYMBOL) {
if (level > typedef_savelevel) {
PUT(PARSER_REF_SYM, token, lineno, sp);
} else {
/* put latest token if any */
if (savetok[0]) {
PUT(PARSER_REF_SYM, savetok, savelineno, sp);
}
/* save lastest token */
strlimcpy(savetok, token, sizeof(savetok));
savelineno = lineno;
}
} else if (c == ',' || c == ';') {
if (savetok[0]) {
PUT(PARSER_DEF, savetok, lineno, sp);
savetok[0] = 0;
}
}
if (level == typedef_savelevel && c == ';')
break;
}
if (param->flags & PARSER_WARNING) {
if (c == EOF)
warning("unexpected eof. [+%d %s]", lineno, curfile);
else if (level != typedef_savelevel)
warning("unmatched () block. (last at level %d.)[+%d %s]", level, lineno, curfile);
}
}
break;
case CPP___ATTRIBUTE__:
process_attribute(param);
break;
default:
break;
}
}
strbuf_close(sb);
if (param->flags & PARSER_WARNING) {
if (level != 0)
warning("unmatched {} block. (last at level %d.)[+%d %s]", level, lineno, curfile);
if (piflevel != 0)
warning("unmatched #if block. (last at level %d.)[+%d %s]", piflevel, lineno, curfile);
}
closetoken();
}
/**
* process_attribute: skip attributes in @CODE{__attribute__((...))}.
*/
static void
process_attribute(const struct parser_param *param)
{
int brace = 0;
int c;
/*
* Skip '...' in __attribute__((...))
* but pick up symbols in it.
*/
while ((c = nexttoken("()", cpp_reserved_word)) != EOF) {
if (c == '(')
brace++;
else if (c == ')')
brace--;
else if (c == SYMBOL) {
PUT(PARSER_REF_SYM, token, lineno, sp);
}
if (brace == 0)
break;
}
}
/**
* function_definition: return if function definition or not.
*
* @return target type
*/
static int
function_definition(const struct parser_param *param)
{
int c;
int brace_level;
brace_level = 0;
while ((c = nexttoken("()", cpp_reserved_word)) != EOF) {
switch (c) {
case SHARP_IFDEF:
case SHARP_IFNDEF:
case SHARP_IF:
case SHARP_ELIF:
case SHARP_ELSE:
case SHARP_ENDIF:
condition_macro(param, c);
continue;
default:
break;
}
if (c == '('/* ) */)
brace_level++;
else if (c == /* ( */')') {
if (--brace_level == 0)
break;
}
/* pick up symbol */
if (c == SYMBOL)
PUT(PARSER_REF_SYM, token, lineno, sp);
}
if (c == EOF)
return 0;
if (peekc(0) == ';') {
(void)nexttoken(";", NULL);
return 0;
}
brace_level = 0;
while ((c = nexttoken(",;[](){}=", cpp_reserved_word)) != EOF) {
switch (c) {
case SHARP_IFDEF:
case SHARP_IFNDEF:
case SHARP_IF:
case SHARP_ELIF:
case SHARP_ELSE:
case SHARP_ENDIF:
condition_macro(param, c);
continue;
case CPP___ATTRIBUTE__:
process_attribute(param);
continue;
default:
break;
}
if (c == '('/* ) */ || c == '[')
brace_level++;
else if (c == /* ( */')' || c == ']')
brace_level--;
else if (brace_level == 0 && (c == ';' || c == ','))
break;
else if (c == '{' /* } */) {
pushbacktoken();
return 1;
} else if (c == /* { */'}') {
pushbacktoken();
break;
} else if (c == '=')
break;
/* pick up symbol */
if (c == SYMBOL)
PUT(PARSER_REF_SYM, token, lineno, sp);
}
return 0;
}
/**
* condition_macro:
*
* @param[in] param
* @param[in] cc token
*/
static void
condition_macro(const struct parser_param *param, int cc)
{
cur = &pifstack[piflevel];
if (cc == SHARP_IFDEF || cc == SHARP_IFNDEF || cc == SHARP_IF) {
DBG_PRINT(piflevel, "#if");
if (++piflevel >= MAXPIFSTACK)
die("#if pifstack over flow. [%s]", curfile);
++cur;
cur->start = level;
cur->end = -1;
cur->if0only = 0;
if (peekc(0) == '0')
cur->if0only = 1;
else if ((cc = nexttoken(NULL, cpp_reserved_word)) == SYMBOL && !strcmp(token, "notdef"))
cur->if0only = 1;
else
pushbacktoken();
} else if (cc == SHARP_ELIF || cc == SHARP_ELSE) {
DBG_PRINT(piflevel - 1, "#else");
if (cur->end == -1)
cur->end = level;
else if (cur->end != level && (param->flags & PARSER_WARNING))
warning("uneven level. [+%d %s]", lineno, curfile);
level = cur->start;
cur->if0only = 0;
} else if (cc == SHARP_ENDIF) {
int minus = 0;
--piflevel;
if (piflevel < 0) {
minus = 1;
piflevel = 0;
}
DBG_PRINT(piflevel, "#endif");
if (minus) {
if (param->flags & PARSER_WARNING)
warning("unmatched #if block. reseted. [+%d %s]", lineno, curfile);
} else {
if (cur->if0only)
level = cur->start;
else if (cur->end != -1) {
if (cur->end != level && (param->flags & PARSER_WARNING))
warning("uneven level. [+%d %s]", lineno, curfile);
level = cur->end;
}
}
}
while ((cc = nexttoken(NULL, cpp_reserved_word)) != EOF && cc != '\n') {
if (cc == SYMBOL && strcmp(token, "defined") != 0) {
PUT(PARSER_REF_SYM, token, lineno, sp);
}
}
}
/**
* enumerator_list: process @CODE{"symbol (= expression), ... \}"}
*/
static int
enumerator_list(const struct parser_param *param)
{
int savelevel = level;
int in_expression = 0;
int c = '{';
for (; c != EOF; c = nexttoken("{}(),=", cpp_reserved_word)) {
switch (c) {
case SHARP_IFDEF:
case SHARP_IFNDEF:
case SHARP_IF:
case SHARP_ELIF:
case SHARP_ELSE:
case SHARP_ENDIF:
condition_macro(param, c);
break;
case SYMBOL:
if (in_expression)
PUT(PARSER_REF_SYM, token, lineno, sp);
else
PUT(PARSER_DEF, token, lineno, sp);
break;
case '{':
case '(':
level++;
break;
case '}':
case ')':
if (--level == savelevel)
return c;
break;
case ',':
if (level == savelevel + 1)
in_expression = 0;
break;
case '=':
in_expression = 1;
break;
default:
break;
}
}
return c;
}