/* [<][>][^][v][top][bottom][index][help] */
DEFINITIONS
This source file includes following definitions.
- yacc
- C
- C_family
- process_attribute
- function_definition
- condition_macro
- seems_datatype
/*
* Copyright (c) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2008
* 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 <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <ctype.h>
#ifdef HAVE_LIMITS_H
#include <limits.h>
#endif
#include <stdio.h>
#ifdef STDC_HEADERS
#include <stdlib.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#else
#include <strings.h>
#endif
#include "gctags.h"
#include "defined.h"
#include "die.h"
#include "strbuf.h"
#include "strlimcpy.h"
#include "token.h"
#include "c_res.h"
static void C_family(const char *, int);
static void process_attribute(int);
static int function_definition(int, char *);
static void condition_macro(int, int);
static int seems_datatype(const char *);
#define IS_TYPE_QUALIFIER(c) ((c) == C_CONST || (c) == C_RESTRICT || (c) == C_VOLATILE)
#define DECLARATIONS 0
#define RULES 1
#define PROGRAMS 2
#define TYPE_C 0
#define TYPE_LEX 1
#define TYPE_YACC 2
/*
* #ifdef stack.
*/
#define MAXPIFSTACK 100
static struct {
short start; /* level when #if block started */
short end; /* level when #if block end */
short if0only; /* #if 0 or notdef only */
} stack[MAXPIFSTACK], *cur;
static int piflevel; /* condition macro level */
static int level; /* brace level */
/*
* yacc: read yacc file and pickup tag entries.
*/
void
yacc(const char *file)
{
C_family(file, TYPE_YACC);
}
/*
* C: read C file and pickup tag entries.
*/
void
C(const char *file)
{
C_family(file, TYPE_C);
}
/*
* i) file source file
* i) type TYPE_C, TYPE_YACC, TYPE_LEX
*/
static void
C_family(const char *file, int type)
{
int c, cc;
int savelevel;
int target;
int startmacro, startsharp;
const char *interested = "{}=;";
STRBUF *sb = strbuf_open(0);
/*
* yacc file format is like the following.
*
* declarations
* %%
* rules
* %%
* programs
*
*/
int yaccstatus = (type == TYPE_YACC) ? DECLARATIONS : PROGRAMS;
int inC = (type == TYPE_YACC) ? 0 : 1; /* 1 while C source */
level = piflevel = 0;
savelevel = -1;
target = (sflag) ? SYM : (rflag) ? REF : DEF;
startmacro = startsharp = 0;
if (!opentoken(file))
die("'%s' cannot open.", file);
cmode = 1; /* allow token like '#xxx' */
crflag = 1; /* require '\n' as a token */
if (type == TYPE_YACC)
ymode = 1; /* allow token like '%xxx' */
while ((cc = nexttoken(interested, c_reserved_word)) != EOF) {
switch (cc) {
case SYMBOL: /* symbol */
if (inC && peekc(0) == '('/* ) */) {
if (isnotfunction(token)) {
if (target == REF && defined(token))
PUT(token, lineno, sp);
} else if (level > 0 || startmacro) {
if (target == REF) {
if (defined(token))
PUT(token, lineno, sp);
} else if (target == SYM) {
if (!defined(token))
PUT(token, lineno, sp);
}
} else if (level == 0 && !startmacro && !startsharp) {
char arg1[MAXTOKEN], savetok[MAXTOKEN], *saveline;
int savelineno = lineno;
strlimcpy(savetok, token, sizeof(savetok));
strbuf_reset(sb);
strbuf_puts(sb, sp);
saveline = strbuf_value(sb);
arg1[0] = '\0';
/*
* Guile function entry using guile-snarf is like follows:
*
* SCM_DEFINE (scm_list, "list", 0, 0, 1,
* (SCM objs),
* "Return a list containing OBJS, the arguments to `list'.")
* #define FUNC_NAME s_scm_list
* {
* return objs;
* }
* #undef FUNC_NAME
*
* We should assume the first argument as a function name instead of 'SCM_DEFINE'.
*/
if (function_definition(target, arg1)) {
if (!strcmp(savetok, "SCM_DEFINE") && *arg1)
strlimcpy(savetok, arg1, sizeof(savetok));
if (target == DEF)
PUT(savetok, savelineno, saveline);
} else {
if (target == REF && defined(savetok))
PUT(savetok, savelineno, saveline);
}
}
} else {
if (target == REF) {
if (defined(token))
PUT(token, lineno, sp);
} else if (target == SYM) {
if (!defined(token))
PUT(token, lineno, sp);
}
}
break;
case '{': /* } */
DBG_PRINT(level, "{"); /* } */
if (yaccstatus == RULES && level == 0)
inC = 1;
++level;
if (bflag && atfirst) {
if (wflag && level != 1)
warning("forced level 1 block start by '{' at column 0 [+%d %s].", lineno, curfile); /* } */
level = 1;
}
break;
/* { */
case '}':
if (--level < 0) {
if (wflag)
warning("missing left '{' [+%d %s].", lineno, curfile); /* } */
level = 0;
}
if (eflag && atfirst) {
if (wflag && level != 0) /* { */
warning("forced level 0 block end by '}' at column 0 [+%d %s].", lineno, curfile);
level = 0;
}
if (yaccstatus == RULES && level == 0)
inC = 0;
/* { */
DBG_PRINT(level, "}");
break;
case '\n':
if (startmacro && level != savelevel) {
if (wflag)
warning("different level before and after #define macro. reseted. [+%d %s].", lineno, curfile);
level = savelevel;
}
startmacro = startsharp = 0;
break;
case YACC_SEP: /* %% */
if (level != 0) {
if (wflag)
warning("forced level 0 block end by '%%' [+%d %s].", lineno, curfile);
level = 0;
}
if (yaccstatus == DECLARATIONS) {
if (target == DEF)
PUT("yyparse", lineno, sp);
yaccstatus = RULES;
} else if (yaccstatus == RULES)
yaccstatus = PROGRAMS;
inC = (yaccstatus == PROGRAMS) ? 1 : 0;
break;
case YACC_BEGIN: /* %{ */
if (level != 0) {
if (wflag)
warning("forced level 0 block end by '%%{' [+%d %s].", lineno, curfile);
level = 0;
}
if (inC == 1 && wflag)
warning("'%%{' appeared in C mode. [+%d %s].", lineno, curfile);
inC = 1;
break;
case YACC_END: /* %} */
if (level != 0) {
if (wflag)
warning("forced level 0 block end by '%%}' [+%d %s].", lineno, curfile);
level = 0;
}
if (inC == 0 && wflag)
warning("'%%}' appeared in Yacc mode. [+%d %s].", lineno, curfile);
inC = 0;
break;
case YACC_UNION: /* %union {...} */
if (yaccstatus == DECLARATIONS && target == DEF)
PUT("YYSTYPE", lineno, sp);
break;
/*
* #xxx
*/
case SHARP_DEFINE:
case SHARP_UNDEF:
startmacro = 1;
savelevel = level;
if ((c = nexttoken(interested, c_reserved_word)) != SYMBOL) {
pushbacktoken();
break;
}
if (peekc(1) == '('/* ) */) {
if (target == DEF)
PUT(token, lineno, sp);
while ((c = nexttoken("()", c_reserved_word)) != EOF && c != '\n' && c != /* ( */ ')')
if (c == SYMBOL && target == SYM)
PUT(token, lineno, sp);
if (c == '\n')
pushbacktoken();
} else {
if (target == DEF)
PUT(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, c_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(cc, target);
break;
case SHARP_SHARP: /* ## */
(void)nexttoken(interested, c_reserved_word);
break;
case C_STRUCT:
case C_ENUM:
case C_UNION:
c = nexttoken(interested, c_reserved_word);
if (c == SYMBOL) {
if (peekc(0) == '{') /* } */ {
if (target == DEF)
PUT(token, lineno, sp);
} else if (target == REF) {
if (defined(token))
PUT(token, lineno, sp);
} else if (target == SYM) {
if (!defined(token))
PUT(token, lineno, sp);
}
c = nexttoken(interested, c_reserved_word);
}
if (c == '{' /* } */ && cc == C_ENUM) {
int savelevel = level;
for (; c != EOF; c = nexttoken(interested, c_reserved_word)) {
switch (c) {
case SHARP_IFDEF:
case SHARP_IFNDEF:
case SHARP_IF:
case SHARP_ELIF:
case SHARP_ELSE:
case SHARP_ENDIF:
condition_macro(c, target);
continue;
default:
break;
}
if (c == '{')
level++;
else if (c == '}') {
if (--level == savelevel)
break;
} else if (c == SYMBOL) {
if (target == DEF)
PUT(token, lineno, sp);
}
}
} else {
pushbacktoken();
}
break;
/* control statement check */
case C_BREAK:
case C_CASE:
case C_CONTINUE:
case C_DEFAULT:
case C_DO:
case C_ELSE:
case C_FOR:
case C_GOTO:
case C_IF:
case C_RETURN:
case C_SWITCH:
case C_WHILE:
if (wflag && !startmacro && level == 0)
warning("Out of function. %8s [+%d %s]", token, lineno, curfile);
break;
case C_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 type qualifiers */
do {
c = nexttoken("{}(),;", c_reserved_word);
} while (IS_TYPE_QUALIFIER(c) || c == '\n');
if (wflag && c == EOF) {
warning("unexpected eof. [+%d %s]", lineno, curfile);
break;
} else if (c == C_ENUM || c == C_STRUCT || c == C_UNION) {
char *interest_enum = "{},;";
int c_ = c;
c = nexttoken(interest_enum, c_reserved_word);
/* read enum name if exist */
if (c == SYMBOL) {
if (peekc(0) == '{') /* } */ {
if (target == DEF)
PUT(token, lineno, sp);
} else if (target == REF) {
if (defined(token))
PUT(token, lineno, sp);
} else if (target == SYM) {
if (!defined(token))
PUT(token, lineno, sp);
}
c = nexttoken(interest_enum, c_reserved_word);
}
for (; c != EOF; c = nexttoken(interest_enum, c_reserved_word)) {
switch (c) {
case SHARP_IFDEF:
case SHARP_IFNDEF:
case SHARP_IF:
case SHARP_ELIF:
case SHARP_ELSE:
case SHARP_ENDIF:
condition_macro(c, target);
continue;
default:
break;
}
if (c == ';' && level == typedef_savelevel) {
if (savetok[0] && target == DEF)
PUT(savetok, savelineno, sp);
break;
} else if (c == '{')
level++;
else if (c == '}') {
if (--level == typedef_savelevel)
break;
} else if (c == SYMBOL) {
if (c_ == C_ENUM) {
if (target == DEF && level > typedef_savelevel)
PUT(token, lineno, sp);
if (target == SYM && level == typedef_savelevel && !defined(token))
PUT(token, lineno, sp);
} else {
if (target == REF) {
if (level > typedef_savelevel && defined(token))
PUT(token, lineno, sp);
} else if (target == SYM) {
if (!defined(token))
PUT(token, lineno, sp);
} else if (target == DEF) {
/* save lastest token */
strlimcpy(savetok, token, sizeof(savetok));
savelineno = lineno;
}
}
}
}
if (c == ';')
break;
if (wflag && c == EOF) {
warning("unexpected eof. [+%d %s]", lineno, curfile);
break;
}
} else if (c == SYMBOL) {
if (target == REF && defined(token))
PUT(token, lineno, sp);
if (target == SYM && !defined(token))
PUT(token, lineno, sp);
}
savetok[0] = 0;
while ((c = nexttoken("(),;", c_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(c, target);
continue;
default:
break;
}
if (c == '(')
level++;
else if (c == ')')
level--;
else if (c == SYMBOL) {
if (level > typedef_savelevel) {
if (target == SYM)
PUT(token, lineno, sp);
} else {
/* put latest token if any */
if (savetok[0]) {
if (target == SYM)
PUT(savetok, savelineno, sp);
}
/* save lastest token */
strlimcpy(savetok, token, sizeof(savetok));
savelineno = lineno;
}
} else if (c == ',' || c == ';') {
if (savetok[0]) {
if (target == DEF)
PUT(savetok, lineno, sp);
savetok[0] = 0;
}
}
if (level == typedef_savelevel && c == ';')
break;
}
if (wflag) {
if (c == EOF)
warning("unexpected eof. [+%d %s]", lineno, curfile);
else if (level != typedef_savelevel)
warning("() block unmatched. (last at level %d.)[+%d %s]", level, lineno, curfile);
}
}
break;
case C___ATTRIBUTE__:
process_attribute(target);
break;
default:
break;
}
}
strbuf_close(sb);
if (wflag) {
if (level != 0)
warning("{} block unmatched. (last at level %d.)[+%d %s]", level, lineno, curfile);
if (piflevel != 0)
warning("#if block unmatched. (last at level %d.)[+%d %s]", piflevel, lineno, curfile);
}
closetoken();
}
/*
* process_attribute: skip attributes in __attribute__((...)).
*
* r) target type
*/
static void
process_attribute(int target)
{
int brace = 0;
int c;
/*
* Skip '...' in __attribute__((...))
* but pick up symbols in it.
*/
while ((c = nexttoken("()", c_reserved_word)) != EOF) {
if (c == '(')
brace++;
else if (c == ')')
brace--;
else if (c == SYMBOL) {
if (target == REF) {
if (defined(token))
PUT(token, lineno, sp);
} else if (target == SYM) {
if (!defined(token))
PUT(token, lineno, sp);
}
}
if (brace == 0)
break;
}
}
/*
* function_definition: return if function definition or not.
*
* i) target DEF, REF, SYMBOL
* o) arg1 the first argument
* r) target type
*/
static int
function_definition(int target, char arg1[MAXTOKEN])
{
int c;
int brace_level, isdefine;
int accept_arg1 = 0;
brace_level = isdefine = 0;
while ((c = nexttoken("()", c_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(c, target);
continue;
default:
break;
}
if (c == '('/* ) */)
brace_level++;
else if (c == /* ( */')') {
if (--brace_level == 0)
break;
}
/* pick up symbol */
if (c == SYMBOL) {
if (accept_arg1 == 0) {
accept_arg1 = 1;
strlimcpy(arg1, token, MAXTOKEN);
}
if (target == REF) {
if (seems_datatype(token) && defined(token))
PUT(token, lineno, sp);
} else if (target == SYM) {
if (!seems_datatype(token) || !defined(token))
PUT(token, lineno, sp);
}
}
}
if (c == EOF)
return 0;
brace_level = 0;
while ((c = nexttoken(",;[](){}=", c_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(c, target);
continue;
case C___ATTRIBUTE__:
process_attribute(target);
continue;
default:
break;
}
if (c == '('/* ) */ || c == '[')
brace_level++;
else if (c == /* ( */')' || c == ']')
brace_level--;
else if (brace_level == 0
&& ((c == SYMBOL && strcmp(token, "__THROW")) || IS_RESERVED_WORD(c)))
isdefine = 1;
else if (c == ';' || c == ',') {
if (!isdefine)
break;
} else if (c == '{' /* } */) {
pushbacktoken();
return 1;
} else if (c == /* { */'}')
break;
else if (c == '=')
break;
/* pick up symbol */
if (c == SYMBOL) {
if (target == REF) {
if (seems_datatype(token) && defined(token))
PUT(token, lineno, sp);
} else if (target == SYM) {
if (!seems_datatype(token) || !defined(token))
PUT(token, lineno, sp);
}
}
}
return 0;
}
/*
* condition_macro:
*
* i) cc token
* i) target current target
*/
static void
condition_macro(int cc, int target)
{
cur = &stack[piflevel];
if (cc == SHARP_IFDEF || cc == SHARP_IFNDEF || cc == SHARP_IF) {
DBG_PRINT(piflevel, "#if");
if (++piflevel >= MAXPIFSTACK)
die("#if stack 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, c_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 && wflag)
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 (wflag)
warning("#if block unmatched. reseted. [+%d %s]", lineno, curfile);
} else {
if (cur->if0only)
level = cur->start;
else if (cur->end != -1) {
if (cur->end != level && wflag)
warning("uneven level. [+%d %s]", lineno, curfile);
level = cur->end;
}
}
}
while ((cc = nexttoken(NULL, c_reserved_word)) != EOF && cc != '\n') {
if (cc == SYMBOL && strcmp(token, "defined") != 0) {
if (target == REF) {
if (defined(token))
PUT(token, lineno, sp);
} else if (target == SYM) {
if (!defined(token))
PUT(token, lineno, sp);
}
}
}
}
/*
* seems_datatype: decide whether or not it is a data type.
*
* i) token token
* r) 0: not data type, 1: data type
*/
static int
seems_datatype(const char *token)
{
int length = strlen(token);
const char *p = token + length;
if (length > 2 && strcmp(p - 2, "_t") == 0)
return 1;
for (p = token; *p; p++)
if (islower(*p))
return 0;
return 1;
}