root/gtags-parser/Cpp.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. Cpp
  2. process_attribute
  3. function_definition
  4. condition_macro
  5. 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 "locatestring.h"
#include "strbuf.h"
#include "strlimcpy.h"
#include "token.h"
#include "cpp_res.h"

static void process_attribute(int);
static int function_definition(int);
static int seems_datatype(const char *);
static void condition_macro(int, int);

#define MAXCOMPLETENAME 1024            /* max size of complete name of class */
#define MAXCLASSSTACK   100             /* max size of class stack */
#define IS_CV_QUALIFIER(c)      ((c) == CPP_CONST || (c) == CPP_VOLATILE)

/*
 * #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 */
} 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 char *file)
{
        int c, cc;
        int savelevel;
        int target;
        int startclass, startthrow, startmacro, startsharp, startequal;
        char classname[MAXTOKEN];
        char completename[MAXCOMPLETENAME];
        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;
        target = (sflag) ? SYM : (rflag) ? REF : DEF;
        startclass = startthrow = startmacro = startsharp = startequal = 0;

        if (!opentoken(file))
                die("'%s' cannot open.", 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) {
                                if (target == REF && defined(token))
                                        PUT(token, lineno, sp);
                        } else if (peekc(0) == '('/* ) */) {
                                if (isnotfunction(token)) {
                                        if (target == REF && defined(token))
                                                PUT(token, lineno, sp);
                                } else if (level > stack[classlevel].level || startequal || 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 == 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(target)) {
                                                /* ignore constructor */
                                                if (target == DEF && strcmp(stack[classlevel].classname, savetok))
                                                        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 CPP_USING:
                        if ((c = nexttoken(interested, cpp_reserved_word)) == CPP_NAMESPACE) {
                                if ((c = nexttoken(interested, cpp_reserved_word)) == SYMBOL) {
                                        if (target == REF)
                                                PUT(token, lineno, sp);
                                } else {
                                        if (wflag)
                                                warning("missing namespace name. [+%d %s].", lineno, curfile);
                                        pushbacktoken();
                                }
                        } else
                                pushbacktoken();
                        /* FALLTHROUGH */
                case CPP_NAMESPACE:
                        if (peekc(0) != '{') /* } */ {
                                if ((c = nexttoken(interested, cpp_reserved_word)) == SYMBOL)
                                        if (target == DEF)
                                                PUT(token, lineno, sp);
                        }
                        while ((c = nexttoken(interested, cpp_reserved_word)) == '\n')
                                ;
                        /*
                         * Namespace block doesn't have any influence on level.
                         */
                        if (c == '{') /* } */ {
                                namespacelevel++;
                        } else {
                                if (wflag)
                                        warning("missing namespace block. [+%d %s](0x%x).", lineno, curfile, c);
                        }
                        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;
                                        if (target == DEF)
                                                PUT(token, lineno, sp);
                                }
                        }
                        break;
                case '{':  /* } */
                        DBG_PRINT(level, "{"); /* } */
                        ++level;
                        if (bflag && atfirst) {
                                if (wflag && 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++ = '.';
                                stack[classlevel].classname = p;
                                while (*q)
                                        *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 (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 (level < stack[classlevel].level)
                                *(stack[--classlevel].terminate) = 0;
                        /* { */
                        DBG_PRINT(level, "}");
                        break;
                case '=':
                        startequal = 1;
                        break;
                case ';':
                        startthrow = startequal = 0;
                        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;
                /*
                 * #xxx
                 */
                case SHARP_DEFINE:
                case SHARP_UNDEF:
                        startmacro = 1;
                        savelevel = level;
                        if ((c = nexttoken(interested, cpp_reserved_word)) != SYMBOL) {
                                pushbacktoken();
                                break;
                        }
                        if (peekc(1) == '('/* ) */) {
                                if (target == DEF)
                                        PUT(token, lineno, sp);
                                while ((c = nexttoken("()", cpp_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, 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(cc, target);
                        break;
                case SHARP_SHARP:               /* ## */
                        (void)nexttoken(interested, cpp_reserved_word);
                        break;
                case CPP_NEW:
                        if ((c = nexttoken(interested, cpp_reserved_word)) == SYMBOL)
                                if (target == REF && defined(token))
                                        PUT(token, lineno, sp);
                        break;
                case CPP_STRUCT:
                case CPP_ENUM:
                case CPP_UNION:
                        c = nexttoken(interested, cpp_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, cpp_reserved_word);
                        }
                        if (c == '{' /* } */ && cc == CPP_ENUM) {
                                int savelevel = level;

                                for (; c != EOF; c = nexttoken(interested, 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(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;
                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) {
                                                if (target == SYM)
                                                        PUT(token, lineno, sp);
                                        }
                                }
                                if (c == EOF && wflag)
                                        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) {
                                        if (target == SYM)
                                                PUT(token, lineno, sp);
                                }
                        }
                        if (c == EOF && wflag)
                                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 (wflag && !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 (wflag && 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;

                                        c = nexttoken(interest_enum, cpp_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, cpp_reserved_word);
                                        }
                                        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(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_ == CPP_ENUM) {
                                                                if (target == DEF && level > typedef_savelevel)
                                                                        PUT(token, lineno, sp);
                                                                if (target == SYM && level == typedef_savelevel)
                                                                        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("(),;", 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(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 CPP___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("()", cpp_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.
 *
 *      r)      target type
 */
static int
function_definition(int target)
{
        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(c, target);
                        continue;
                default:
                        break;
                }
                if (c == '('/* ) */)
                        brace_level++;
                else if (c == /* ( */')') {
                        if (--brace_level == 0)
                                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);
                        }
                }
        }
        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(c, target);
                        continue;
                case CPP___ATTRIBUTE__:
                        process_attribute(target);
                        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 == /* { */'}')
                        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 = &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 && 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, cpp_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;
}

/* [<][>][^][v][top][bottom][index][help] */