root/usr/src/tools/smatch/src/ctags.c
/*
 * Sparse Ctags
 *
 * Ctags generates tags from preprocessing results.
 *
 * Copyright (C) 2006 Christopher Li
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>

#include "parse.h"
#include "scope.h"

static struct symbol_list *taglist = NULL;

static void examine_symbol(struct symbol *sym);

#define MAX(_x,_y) ((_x) > (_y) ? (_x) : (_y))

static int cmp_sym(const void *m, const void *n)
{
        const struct ident *a = ((const struct symbol *)m)->ident;
        const struct ident *b = ((const struct symbol *)n)->ident;
        int ret = strncmp(a->name, b->name, MAX(a->len, b->len));
        if (!ret) {
                const struct position a_pos = ((const struct symbol *)m)->pos;
                const struct position b_pos = ((const struct symbol *)n)->pos;

                ret = strcmp(stream_name(a_pos.stream),
                             stream_name(b_pos.stream));
                if (!ret)
                        return a_pos.line < b_pos.line;
        }
        return ret;
}

static void show_tag_header(FILE *fp)
{
        fprintf(fp, "!_TAG_FILE_FORMAT\t2\t/extended format; --format=1 will not append ;\" to lines/\n");
        fprintf(fp, "!_TAG_FILE_SORTED\t0\t/0=unsorted, 1=sorted, 2=foldcase/\n");
        fprintf(fp, "!_TAG_PROGRAM_AUTHOR\tChristopher Li\t/sparse@chrisli.org/\n");
        fprintf(fp, "!_TAG_PROGRAM_NAME\tSparse Ctags\t//\n");
        fprintf(fp, "!_TAG_PROGRAM_URL\thttp://www.kernel.org/pub/software/devel/sparse/\t/official site/\n");
        fprintf(fp, "!_TAG_PROGRAM_VERSION\t0.01\t//\n");
}

static inline void show_symbol_tag(FILE *fp, struct symbol *sym)
{
        fprintf(fp, "%s\t%s\t%d;\"\t%c\tfile:\n", show_ident(sym->ident),
               stream_name(sym->pos.stream), sym->pos.line, (int)sym->kind);
}

static void show_tags(struct symbol_list *list)
{
        struct symbol *sym;
        struct ident *ident = NULL;
        struct position pos = {};
        static const char *filename;
        FILE *fp;

        if (!list)
                return;

        fp = fopen("tags", "w");
        if (!fp) {
                perror("open tags file");
                return;
        }
        show_tag_header(fp);
        FOR_EACH_PTR(list, sym) {
                if (ident == sym->ident && pos.line == sym->pos.line &&
                    !strcmp(filename, stream_name(sym->pos.stream)))
                        continue;

                show_symbol_tag(fp, sym);
                ident = sym->ident;
                pos = sym->pos;
                filename = stream_name(sym->pos.stream);
        } END_FOR_EACH_PTR(sym);
        fclose(fp);
}

static inline void add_tag(struct symbol *sym)
{
        if (sym->ident && !sym->visited) {
                sym->visited = 1;
                add_symbol(&taglist, sym);
        }
}

static inline void examine_members(struct symbol_list *list)
{
        struct symbol *sym;

        FOR_EACH_PTR(list, sym) {
                sym->kind = 'm';
                examine_symbol(sym);
        } END_FOR_EACH_PTR(sym);
}

static void examine_symbol(struct symbol *sym)
{
        struct symbol *base = sym;

        if (!sym || sym->visited)
                return;
        if (sym->ident && sym->ident->reserved)
                return;
        if (sym->type == SYM_KEYWORD || sym->type == SYM_PREPROCESSOR)
                return;

        add_tag(sym);
        base = sym->ctype.base_type;

        switch (sym->type) {
        case SYM_NODE:
                if (base && base->type == SYM_FN)
                        sym->kind = 'f';
                examine_symbol(base);
                break;
        case SYM_STRUCT:
                sym->kind = 's';
                examine_members(sym->symbol_list);
                break;
        case SYM_UNION:
                sym->kind = 'u';
                examine_members(sym->symbol_list);
                break;
        case SYM_ENUM:
                sym->kind = 'e';
        case SYM_PTR:
        case SYM_TYPEOF:
        case SYM_BITFIELD:
        case SYM_FN:
        case SYM_ARRAY:
                examine_symbol(sym->ctype.base_type);
                break;
        case SYM_BASETYPE:
                break;

        default:
                die("unknown symbol %s namespace:%d type:%d\n", show_ident(sym->ident),
                    sym->namespace, sym->type);
        }
        if (!sym->kind)
                sym->kind = 'v';
        return;
}

static void examine_namespace(struct symbol *sym)
{
        if (sym->visited)
                return;
        if (sym->ident && sym->ident->reserved)
                return;

        switch(sym->namespace) {
        case NS_KEYWORD:
        case NS_PREPROCESSOR:
                return;
        case NS_LABEL:
                sym->kind = 'l';
                break;
        case NS_MACRO:
        case NS_UNDEF:
                sym->kind = 'd';
                break;
        case NS_TYPEDEF:
                sym->kind = 't';
        case NS_SYMBOL:
        case NS_STRUCT:
                examine_symbol(sym);
                break;
        default:
                die("unknown namespace %d symbol:%s type:%d\n", sym->namespace,
                    show_ident(sym->ident), sym->type);
        }
        add_tag(sym);
}

static inline void examine_symbol_list(struct symbol_list *list)
{
        struct symbol *sym;

        if (!list)
                return;
        FOR_EACH_PTR(list, sym) {
                examine_namespace(sym);
        } END_FOR_EACH_PTR(sym);
}

int main(int argc, char **argv)
{
        struct string_list *filelist = NULL;
        char *file;

        examine_symbol_list(sparse_initialize(argc, argv, &filelist));
        FOR_EACH_PTR(filelist, file) {
                sparse(file);
                examine_symbol_list(file_scope->symbols);
        } END_FOR_EACH_PTR(file);
        examine_symbol_list(global_scope->symbols);
        sort_list((struct ptr_list **)&taglist, cmp_sym);
        show_tags(taglist);
        return 0;
}