Blob Blame History Raw
/**********************************************************/
/*  apimagic: cparser-based API normalization utility     */
/*  Copyright (C) 2015--2016  Z. Gilboa                   */
/*  Released under GPLv2 and GPLv3; see COPYING.APIMAGIC. */
/**********************************************************/

#include <stdio.h>

#include <cparser/ast/ast_t.h>
#include <cparser/ast/type.h>
#include <cparser/ast/type_t.h>
#include <cparser/ast/entity_t.h>
#include <cparser/ast/symbol_t.h>

#include <apimagic/apimagic.h>
#include "apimagic_driver_impl.h"

static int output_string(
	FILE *		fout,
	const char *	fmt,
	const char *	string,
	const char *	brace,
	int *		len)
{
	int ret = fout
		? fprintf(fout,fmt,string,brace)
		: snprintf(0,0,fmt,string,brace);

	if (len && (ret > 0))
		*len += ret;

	return ret;
}

static int output_compound(
	union entity_t const *		entity,
	int				depth,
	const struct amgc_layout *	layout,
	FILE *				fout)
{
	struct compound_t const *	compound;
	type_qualifiers_t		tquals;
	union entity_t const *		subentity;
	const union type_t *		type;
	const struct declaration_t *	decl;
	const char *			fmt;
	const char *			name;
	const char *			brace;
	bool				ftabs;
	bool				fspace;
	int				len;
	int				width;
	int				ptrdepth;
	int				symwidth;
	int				i;

	if (entity->base.symbol && (depth == 0)) {
		name  = entity->base.symbol->string;
		brace = " {";
	} else {
		name  = "";
		brace = "{";
	}

	compound = 0;

	if (entity->kind == ENTITY_STRUCT)
		fmt = "struct %s%s\n";
	else if (entity->kind == ENTITY_UNION)
		fmt = "union %s%s\n";
	else if (entity->kind != ENTITY_COMPOUND_MEMBER)
		return -1;
	else if (entity->declaration.type->kind == TYPE_COMPOUND_STRUCT)
		fmt = "struct %s%s\n";
	else if (entity->declaration.type->kind == TYPE_COMPOUND_UNION)
		fmt = "union %s%s\n";
	else if (entity->declaration.type->kind == TYPE_POINTER) {
		type = entity->declaration.type;

		for (; type->kind == TYPE_POINTER; )
			type = type->pointer.points_to;

		compound = type->compound.compound;
		entity   = compound->members.first_entity;

		if (type->kind == TYPE_COMPOUND_STRUCT)
			fmt = "struct %s%s\n";
		else if (type->kind == TYPE_COMPOUND_UNION)
			fmt = "union %s%s\n";
		else
			return -1;
	} else
		return -1;


	if (compound)
		(void)0;
	else if (entity->kind == ENTITY_COMPOUND_MEMBER) {
		compound = entity->declaration.type->compound.compound;
		entity = compound->members.first_entity;
	} else {
		compound = &entity->compound;
		entity = compound->members.first_entity;
	}


	if (depth && fout && (fputc('\n',fout) < 0))
		return -1;

	for (i=0; i<depth; i++)
		if (output_string(fout,"\t","","",0) < 0)
			return -1;

	if (output_string(fout,fmt,name,brace,&len) < 0)
		return -1;


	for (width=0,depth++; entity; entity=entity->base.next) {
		type = entity->declaration.type;
		len  = 0;

		while (type->kind == TYPE_ARRAY)
			type = type->array.element_type;

		for (ptrdepth=0; type->kind==TYPE_POINTER; ptrdepth++)
			type = type->pointer.points_to;

		tquals	= type->base.qualifiers;
		ftabs	= true;

		switch (type->kind) {
			case TYPE_TYPEDEF:
				fmt = "%s ";
				name = type->typedeft.typedefe->base.symbol->string;
				break;

			case TYPE_ATOMIC:
				fmt = "%s ";
				name = get_atomic_kind_name(type->atomic.akind);
				break;

			case TYPE_COMPOUND_STRUCT:
			case TYPE_COMPOUND_UNION:
				compound  = type->compound.compound;
				subentity = compound->members.first_entity;
				decl      = type->typedeft.typedefe;

				if (!subentity || decl->base.symbol) {
					name = decl->base.symbol->string;
					fmt  = (type->kind == TYPE_COMPOUND_STRUCT)
						? "struct %s " : "union %s ";
				} else {
					ftabs = false;
					name  = "";
					fmt   = "";

					if (output_compound(
							entity,depth,
							layout,fout) < 0)
						return -1;
				}

				break;

			case TYPE_VOID:
				fmt = "%s ";
				name = "void";
				break;

			default:
				fmt = "";
				name = "";

				if (fout)
					fprintf(fout,"UNHANDLED TYPE! %d ",
						type->kind);

				break;
		}

		if (ftabs)
			for (i=0; i<depth; i++)
				if (output_string(fout,"\t","","",0) < 0)
					return -1;

		if (tquals & TYPE_QUALIFIER_CONST)
			if (output_string(fout,"const ","","",&len) < 0)
				return -1;

		if (output_string(fout,fmt,name,"",&len) < 0)
			return -1;

		for (fspace=ptrdepth; ptrdepth; ptrdepth--) {
			type = entity->declaration.type;

			while (type->kind == TYPE_ARRAY)
				type = type->array.element_type;

			for (i=0; i<ptrdepth; i++)
				type = type->pointer.points_to;

			if (type->base.qualifiers & TYPE_QUALIFIER_CONST)
				if (type->kind == TYPE_POINTER)
					if (output_string(fout," const ",
							"","",&len) < 0)
						return -1;

			if (output_string(fout,"*","","",&len) < 0)
				return -1;
		}

		if (fspace)
			len++;

		if (fout) {
			symwidth = layout->symwidth;

			symwidth += layout->tabwidth;
			symwidth &= (~(layout->tabwidth-1));

			len &= (~(layout->tabwidth-1));

			while (len < symwidth) {
				if (fputc('\t',fout) < 0)
					return -1;
				else
					len += layout->tabwidth;
			}
		} else if (len > width)
			width = len;

		if (output_string(fout,"%s",
				entity->base.symbol->string,
				"",0) < 0)
			return -1;

		type = entity->declaration.type;

		while (fout && type->kind == TYPE_ARRAY) {
			if (fprintf(fout,
					type->array.size ? "[%zu]" : "[]",
					type->array.size) < 0)
				return -1;

			type = type->array.element_type;
		}

		if (fout && fputs(";\n",fout) < 0)
			return -1;

		if (!ftabs && fout && entity->base.next)
			if (fputc('\n',fout) < 0)
				return -1;
	}

	if (!fout)
		return width;

	if (--depth) {
		for (i=0; i<depth; i++)
			if (output_string(fout,"\t","","",0) < 0)
				return -1;

		if (output_string(fout,"} ","","",0) < 0)
			return -1;
	} else {
		if (output_string(fout,"};\n","","",0) < 0)
			return -1;
	}

	return 0;
}

static int output_compound_entity(
	const struct amgc_unit_ctx *	uctx,
	const struct amgc_entity *	aentity,
	const struct amgc_layout *	layout,
	FILE *				fout)
{
	struct amgc_layout elayout;

	if (layout && layout->symwidth)
		return output_compound(aentity->entity,0,layout,fout);

	if (layout)
		memcpy(&elayout,layout,sizeof(elayout));
	else
		memset(&elayout,0,sizeof(elayout));

	if ((elayout.symwidth = output_compound(aentity->entity,0,layout,0)) < 0)
		return -1;

	if (elayout.tabwidth == 0)
		elayout.tabwidth = AMGC_TAB_WIDTH;

	if (output_compound(aentity->entity,0,&elayout,fout) < 0)
		return -1;

	return 0;
}

int amgc_output_compound(
	const struct amgc_unit_ctx *	uctx,
	const struct amgc_entity *	aentity,
	const struct amgc_layout *	layout,
	FILE *				fout)
{
	union entity_t const * entity;

	entity = aentity->entity;

	if ((entity->kind == ENTITY_STRUCT) || (entity->kind == ENTITY_UNION))
		return output_compound_entity(uctx,aentity,layout,fout);
	else
		return -1;
}

int amgc_output_struct(
	const struct amgc_unit_ctx *	uctx,
	const struct amgc_entity *	aentity,
	const struct amgc_layout *	layout,
	FILE *				fout)
{
	union entity_t const * entity;

	entity = aentity->entity;

	if (entity->kind == ENTITY_STRUCT)
		return output_compound_entity(uctx,aentity,layout,fout);
	else
		return -1;
}

int amgc_output_union(
	const struct amgc_unit_ctx *	uctx,
	const struct amgc_entity *	aentity,
	const struct amgc_layout *	layout,
	FILE *				fout)
{
	union entity_t const * entity;

	entity = aentity->entity;

	if (entity->kind == ENTITY_UNION)
		return output_compound_entity(uctx,aentity,layout,fout);
	else
		return -1;
}

int  amgc_output_unit_structs(
	const struct amgc_unit_ctx *	uctx,
	const struct amgc_layout *	layout,
	FILE *				fout)
{
	const struct amgc_entity * aentity;

	for (aentity=uctx->entities->structs; aentity->entity; aentity++)
		if (output_compound_entity(uctx,aentity,layout,fout))
			return -1;

	return 0;
}

int  amgc_output_unit_unions(
	const struct amgc_unit_ctx *	uctx,
	const struct amgc_layout *	layout,
	FILE *				fout)
{
	const struct amgc_entity * aentity;

	for (aentity=uctx->entities->unions; aentity->entity; aentity++)
		if (output_compound_entity(uctx,aentity,layout,fout))
			return -1;

	return 0;
}