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 <stdint.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/mman.h>


#include <cparser/driver/driver.h>
#include <cparser/driver/c_driver.h>

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

static int amgc_free_unit_ctx_impl(struct amgc_unit_ctx_impl * ctx, int ret)
{
	if (ctx) {
		if (ctx->entities)
			amgc_free_unit_entities(ctx->entities);

		amgc_unmap_input(&ctx->map);
		free(ctx);
	}

	return ret;
}

static FILE * amgc_stdin_to_tmp(const struct amgc_driver_ctx * dctx)
{
	struct amgc_driver_ctx_impl *	ictx;
	uintptr_t			addr;
	int				fdtmp;

	FILE *	ftmp;
	char	buf[4096];
	ssize_t	nread;
	int	ret;

	addr = (uintptr_t)dctx - offsetof(struct amgc_driver_ctx_impl,ctx);
	ictx = (struct amgc_driver_ctx_impl *)addr;

	if (ictx->fdtmpin >= 0) {
		if ((fdtmp = dup(ictx->fdtmpin)) < 0)
			return 0;

		if (!(ftmp = fdopen(fdtmp,"r")))
			close(fdtmp);

		return ftmp;
	}

	if (!(ftmp = tmpfile()))
		return 0;

	if ((ictx->fdtmpin = dup(fileno(ftmp))) < 0) {
		fclose(ftmp);
		return 0;
	}

	nread = read(0,buf,sizeof(buf)-1);

	while (nread) {
		if (nread > 0) {
			buf[nread] = '\0';
			ret = fputs(buf,ftmp);
		} else
			ret = (errno == EINTR) ? 0 : -1;

		if (ret < 0) {
			fclose(ftmp);
			return 0;
		}

		nread = read(0,buf,sizeof(buf)-1);
	}

	if (fflush(ftmp)) {
		fclose(ftmp);
		return 0;
	}

	return ftmp;
}

static bool amgc_cparser_no_op(
	compilation_env_t *	env,
	compilation_unit_t *	unit)
{
	(void)env;
	(void)unit;
	return true;
}

static void amgc_init_cparser_unit(void)
{
	set_default_handlers();

	/* parse only */
	set_unit_handler(COMPILATION_UNIT_AST,
			build_firm_ir,true);

	/* assign no-op handler */
	set_unit_handler(COMPILATION_UNIT_PREPROCESSED_ASSEMBLER,
			amgc_cparser_no_op,true);

	set_unit_handler(COMPILATION_UNIT_OBJECT,
			amgc_cparser_no_op,true);
}

int amgc_get_unit_ctx(
	const struct amgc_driver_ctx *	dctx,
	const char *			path,
	struct amgc_unit_ctx **		pctx)
{
	struct amgc_unit_ctx_impl *	ctx;
	FILE *				ftmp;
	int				fd;

	amgc_init_cparser_unit();

	if (!dctx)
		return AMGC_CUSTOM_ERROR(
			dctx,AMGC_ERR_NULL_CONTEXT);

	else if (!(ctx = calloc(1,sizeof(*ctx))))
		return AMGC_SYSTEM_ERROR(dctx);

	amgc_driver_set_ectx(
		dctx,0,path);

	if (strcmp(path,"-"))
		fd = -1;

	else if (!(ftmp = amgc_stdin_to_tmp(dctx)))
		return amgc_free_unit_ctx_impl(
			ctx,AMGC_SYSTEM_ERROR(dctx));

	else if ((fd = dup(fileno(ftmp))) < 0)
		return amgc_free_unit_ctx_impl(
			ctx,AMGC_SYSTEM_ERROR(dctx));

	else
		ctx->ccunit.input = ftmp;

	if (amgc_map_input(dctx,fd,path,PROT_READ,&ctx->map))
		return amgc_free_unit_ctx_impl(
			ctx,AMGC_NESTED_ERROR(dctx));

	if (fd > 0)
		close(fd);

	/* compilation unit */
	ctx->ccunit.name		= path;
	ctx->ccunit.original_name	= path;
	ctx->ccunit.type		= COMPILATION_UNIT_C;
	ctx->ccunit.standard		= dctx->cctx->std;

	/* parse, generate ast, generate ir */
	if ((process_unit(ctx->cctx.ccenv,&ctx->ccunit)) == false) {
		if (ctx->ccunit.input)
			fclose(ctx->ccunit.input);
		return amgc_free_unit_ctx_impl(
			ctx,AMGC_CUSTOM_ERROR(dctx,AMGC_ERR_FLOW_ERROR));
	}

	memcpy(&ctx->cctx,dctx->cctx,
		sizeof(ctx->cctx));

	ctx->dctx	    = dctx;
	ctx->path	    = path;
	ctx->uctx.path	    = &ctx->path;
	ctx->uctx.map	    = &ctx->map;
	ctx->uctx.cctx	    = &ctx->cctx;
	ctx->uctx.meta	    = &ctx->meta;
	ctx->uctx.ccunit    = &ctx->ccunit;

	if (amgc_get_unit_entities(&ctx->uctx,&ctx->meta,&ctx->entities))
		return amgc_free_unit_ctx_impl(
			ctx,AMGC_CUSTOM_ERROR(dctx,AMGC_ERR_FLOW_ERROR));

	ctx->uctx.entities  = ctx->entities;
	*pctx = &ctx->uctx;
	return 0;
}

void amgc_free_unit_ctx(struct amgc_unit_ctx * ctx)
{
	struct amgc_unit_ctx_impl *	ictx;
	uintptr_t			addr;

	if (ctx) {
		addr = (uintptr_t)ctx - offsetof(struct amgc_unit_ctx_impl,uctx);
		ictx = (struct amgc_unit_ctx_impl *)addr;
		amgc_free_unit_ctx_impl(ictx,0);
	}
}