root/usr/src/tools/smatch/src/c2xml.c
/*
 * Sparse c2xml
 *
 * Dumps the parse tree as an xml document
 *
 * Copyright (C) 2007 Rob Taylor
 *
 * 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 <assert.h>
#include <libxml/parser.h>
#include <libxml/tree.h>

#include "expression.h"
#include "parse.h"
#include "scope.h"
#include "symbol.h"

static xmlDocPtr doc = NULL;       /* document pointer */
static xmlNodePtr root_node = NULL;/* root node pointer */
static int idcount = 0;

static void examine_symbol(struct symbol *sym, xmlNodePtr node);

static xmlAttrPtr newProp(xmlNodePtr node, const char *name, const char *value)
{
        return xmlNewProp(node, BAD_CAST name, BAD_CAST value);
}

static xmlAttrPtr newNumProp(xmlNodePtr node, const char *name, int value)
{
        char buf[256];
        snprintf(buf, 256, "%d", value);
        return newProp(node, name, buf);
}

static xmlAttrPtr newIdProp(xmlNodePtr node, const char *name, unsigned int id)
{
        char buf[256];
        snprintf(buf, 256, "_%d", id);
        return newProp(node, name, buf);
}

static xmlNodePtr new_sym_node(struct symbol *sym, const char *name, xmlNodePtr parent)
{
        xmlNodePtr node;
        const char *ident = show_ident(sym->ident);

        assert(name != NULL);
        assert(sym != NULL);
        assert(parent != NULL);

        node = xmlNewChild(parent, NULL, BAD_CAST "symbol", NULL);

        newProp(node, "type", name);

        newIdProp(node, "id", idcount);

        if (sym->ident && ident)
                newProp(node, "ident", ident);
        newProp(node, "file", stream_name(sym->pos.stream));

        newNumProp(node, "start-line", sym->pos.line);
        newNumProp(node, "start-col", sym->pos.pos);

        if (sym->endpos.type) {
                newNumProp(node, "end-line", sym->endpos.line);
                newNumProp(node, "end-col", sym->endpos.pos);
                if (sym->pos.stream != sym->endpos.stream)
                        newProp(node, "end-file", stream_name(sym->endpos.stream));
        }
        sym->aux = node;

        idcount++;

        return node;
}

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

        FOR_EACH_PTR(list, sym) {
                examine_symbol(sym, node);
        } END_FOR_EACH_PTR(sym);
}

static void examine_modifiers(struct symbol *sym, xmlNodePtr node)
{
        const char *modifiers[] = {
                        "auto",
                        "register",
                        "static",
                        "extern",
                        "const",
                        "volatile",
                        "signed",
                        "unsigned",
                        "char",
                        "short",
                        "long",
                        "long-long",
                        "typedef",
                        NULL,
                        NULL,
                        NULL,
                        NULL,
                        NULL,
                        "inline",
                        "addressable",
                        "nocast",
                        "noderef",
                        "accessed",
                        "toplevel",
                        "label",
                        "assigned",
                        "type-type",
                        "safe",
                        "user-type",
                        "force",
                        "explicitly-signed",
                        "bitwise"};

        int i;

        if (sym->namespace != NS_SYMBOL)
                return;

        /*iterate over the 32 bit bitfield*/
        for (i=0; i < 32; i++) {
                if ((sym->ctype.modifiers & 1<<i) && modifiers[i])
                        newProp(node, modifiers[i], "1");
        }
}

static void
examine_layout(struct symbol *sym, xmlNodePtr node)
{
        examine_symbol_type(sym);

        newNumProp(node, "bit-size", sym->bit_size);
        newNumProp(node, "alignment", sym->ctype.alignment);
        newNumProp(node, "offset", sym->offset);
        if (is_bitfield_type(sym)) {
                newNumProp(node, "bit-offset", sym->bit_offset);
        }
}

static void examine_symbol(struct symbol *sym, xmlNodePtr node)
{
        xmlNodePtr child = NULL;
        const char *base;
        int array_size;

        if (!sym)
                return;
        if (sym->aux)           /*already visited */
                return;

        if (sym->ident && sym->ident->reserved)
                return;

        child = new_sym_node(sym, get_type_name(sym->type), node);
        examine_modifiers(sym, child);
        examine_layout(sym, child);

        if (sym->ctype.base_type) {
                if ((base = builtin_typename(sym->ctype.base_type)) == NULL) {
                        if (!sym->ctype.base_type->aux) {
                                examine_symbol(sym->ctype.base_type, root_node);
                        }
                        xmlNewProp(child, BAD_CAST "base-type",
                                   xmlGetProp((xmlNodePtr)sym->ctype.base_type->aux, BAD_CAST "id"));
                } else {
                        newProp(child, "base-type-builtin", base);
                }
        }
        if (sym->array_size) {
                /* TODO: modify get_expression_value to give error return */
                array_size = get_expression_value(sym->array_size);
                newNumProp(child, "array-size", array_size);
        }


        switch (sym->type) {
        case SYM_STRUCT:
        case SYM_UNION:
                examine_members(sym->symbol_list, child);
                break;
        case SYM_FN:
                examine_members(sym->arguments, child);
                break;
        case SYM_UNINITIALIZED:
                newProp(child, "base-type-builtin", builtin_typename(sym));
                break;
        default:
                break;
        }
        return;
}

static struct position *get_expansion_end (struct token *token)
{
        struct token *p1, *p2;

        for (p1=NULL, p2=NULL;
             !eof_token(token);
             p2 = p1, p1 = token, token = token->next);

        if (p2)
                return &(p2->pos);
        else
                return NULL;
}

static void examine_macro(struct symbol *sym, xmlNodePtr node)
{
        struct position *pos;

        /* this should probably go in the main codebase*/
        pos = get_expansion_end(sym->expansion);
        if (pos)
                sym->endpos = *pos;
        else
                sym->endpos = sym->pos;

        new_sym_node(sym, "macro", node);
}

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

        switch(sym->namespace) {
        case NS_MACRO:
                examine_macro(sym, root_node);
                break;
        case NS_TYPEDEF:
        case NS_STRUCT:
        case NS_SYMBOL:
                examine_symbol(sym, root_node);
                break;
        case NS_NONE:
        case NS_LABEL:
        case NS_ITERATOR:
        case NS_UNDEF:
        case NS_PREPROCESSOR:
        case NS_KEYWORD:
                break;
        default:
                die("Unrecognised namespace type %d",sym->namespace);
        }

}

static int get_stream_id (const char *name)
{
        int i;
        for (i=0; i<input_stream_nr; i++) {
                if (strcmp(name, stream_name(i))==0)
                        return i;
        }
        return -1;
}

static inline void examine_symbol_list(const char *file, struct symbol_list *list)
{
        struct symbol *sym;
        int stream_id = get_stream_id (file);

        if (!list)
                return;
        FOR_EACH_PTR(list, sym) {
                if (sym->pos.stream == stream_id)
                        examine_namespace(sym);
        } END_FOR_EACH_PTR(sym);
}

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

        doc = xmlNewDoc(BAD_CAST "1.0");
        root_node = xmlNewNode(NULL, BAD_CAST "parse");
        xmlDocSetRootElement(doc, root_node);

/* - A DTD is probably unnecessary for something like this

        dtd = xmlCreateIntSubset(doc, "parse", "http://www.kernel.org/pub/software/devel/sparse/parse.dtd" NULL, "parse.dtd");

        ns = xmlNewNs (root_node, "http://www.kernel.org/pub/software/devel/sparse/parse.dtd", NULL);

        xmlSetNs(root_node, ns);
*/
        symlist = sparse_initialize(argc, argv, &filelist);

        FOR_EACH_PTR(filelist, file) {
                examine_symbol_list(file, symlist);
                sparse_keep_tokens(file);
                examine_symbol_list(file, file_scope->symbols);
                examine_symbol_list(file, global_scope->symbols);
        } END_FOR_EACH_PTR(file);


        xmlSaveFormatFileEnc("-", doc, "UTF-8", 1);
        xmlFreeDoc(doc);
        xmlCleanupParser();

        return 0;
}