Blob Blame History Raw
/**********************************************************/
/*  apimagic: cparser-based API normalization utility     */
/*  Copyright (C) 2015--2021  SysDeer Technologies, LLC   */
/*  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(
	int *		fdout,
	const char *	fmt,
	const char *	string,
	const char *	brace,
	int *		len)
{
	int ret = fdout
		? amgc_dprintf(*fdout,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,
	int *				fdout)
{
	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 && fdout && (amgc_dprintf(*fdout,"\n") < 0))
		return -1;

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

	if (output_string(fdout,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,fdout) < 0)
						return -1;
				}

				break;

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

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

				if (fdout)
					amgc_dprintf(
						*fdout,
						"UNHANDLED TYPE! %d ",
						type->kind);

				break;
		}

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

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

		if (output_string(fdout,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(fdout," const ",
							"","",&len) < 0)
						return -1;

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

		if (fspace)
			len++;

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

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

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

			while (len < symwidth) {
				if (amgc_dprintf(*fdout,"\t") < 0)
					return -1;
				else
					len += layout->tabwidth;
			}
		} else if (len > width)
			width = len;

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

		type = entity->declaration.type;

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

			type = type->array.element_type;
		}

		if (fdout && amgc_dprintf(*fdout,";\n") < 0)
			return -1;

		if (!ftabs && fdout && entity->base.next)
			if (amgc_dprintf(*fdout,"\n") < 0)
				return -1;
	}

	if (!fdout)
		return width;

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

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

	return 0;
}

static int output_compound_entity(
	const struct amgc_driver_ctx *	dctx,
	const struct amgc_unit_ctx *	uctx,
	const struct amgc_entity *	aentity,
	const struct amgc_layout *	layout)
{
	struct amgc_layout elayout;
	int fdout = amgc_driver_fdout(dctx);

	(void)uctx;

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

	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,&fdout) < 0)
		return -1;

	return 0;
}

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

	entity = aentity->entity;

	return ((entity->kind == ENTITY_STRUCT) || (entity->kind == ENTITY_UNION))
		? output_compound_entity(dctx,uctx,aentity,layout)
		: -1;
}

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

	entity = aentity->entity;

	return (entity->kind == ENTITY_STRUCT)
		? output_compound_entity(dctx,uctx,aentity,layout)
		: -1;
}

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

	entity = aentity->entity;

	return (entity->kind == ENTITY_UNION)
		? output_compound_entity(dctx,uctx,aentity,layout)
		: -1;

}

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

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

	return 0;
}

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

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

	return 0;
}