[go: up one dir, main page]

Menu

[904618]: / ____makc.___  Maximize  Restore  History

Download this file

447 lines (369 with data), 9.3 kB

// -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*-
// -----------------------------------------------------------------------------
// ########   ###### #####   #####  ######   ######  ######
// ##     ##  ##     ##  ## ##  ## ##    ## ##    ## ##   ##
// ##     ##  ##     ##   ###   ## ##    ## ##    ## ##    ##
// ########   ####   ##    #    ## ##    ## ##    ## ##    ##
// ##    ##   ##     ##         ## ##    ## ##    ## ##    ##
// ##     ##  ##     ##         ## ##    ## ##    ## ##   ##
// ##      ## ###### ##         ##  ######   ######  ######
//                      http://remood.org/
// -----------------------------------------------------------------------------
// Copyright (C) 2011 GhostlyDeath <ghostlydeath@remood.org>
// -----------------------------------------------------------------------------
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 3
// of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// -----------------------------------------------------------------------------
// DESCRIPTION: Makes a fresh makefile

/***************
*** INCLUDES ***
***************/

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#ifndef __restrict__
	#define __restrict__
#endif

/*****************
*** STRUCTURES ***
*****************/

/* CDefine_t -- C Definition */
typedef struct CDefine_s
{
	char* Define;								// The actual C Define
	char* Value;								// The value of said define
} CDefine_t;

/*************
*** LOCALS ***
*************/

static CDefine_t* l_Defines[2];					// Available defines
static size_t l_NumDefines[2];					// Number of defines

/****************
*** FUNCTIONS ***
****************/

/* AddDefine() -- Define something */
void AddDefine(const int Type, const char* const Define, const char* const Value)
{
	size_t i, j;
	
	// Check
	if (Type < 0 || Type > 1 || !Define || !Value)
		return;
	
	// Look for an empty spot
	for (i = 0; i < l_NumDefines[Type]; i++)
		if (!l_Defines[Type][i].Define)
		{
			j = i;
			break;
		}
	
	// Ran out?
	if (i == l_NumDefines[Type])
	{
		// Reallocate defines
		l_Defines[Type] = realloc(l_Defines[Type], sizeof(*l_Defines[Type]) * (l_NumDefines[Type] + 1));
	
		if (!l_Defines[Type])
		{
			l_NumDefines[Type] = 0;
			return;	// Oops
		}
	
		j = l_NumDefines[Type]++;
	}
	
	// Copy
	l_Defines[Type][j].Define = strdup(Define);
	l_Defines[Type][j].Value = strdup(Value);
}

/* FindDef() -- Finds a definition */
CDefine_t* FindDef(const int Type, const char* const Def)
{
	size_t i;
	
	// Check
	if (Type < 0 || Type > 1 || !Def)
		return NULL;
	
	// Search
	for (i = 0; i < l_NumDefines[Type]; i++)
		if (strcasecmp(Def, l_Defines[Type][i].Define) == 0)
			return &l_Defines[Type][i];
	
	// Failure
	return NULL;
}

/* UnDef() -- Undefine something */
void UnDef(const int Type, const char* const Def)
{
	CDefine_t* Got;
	
	// Check
	if (Type < 0 || Type > 1 || !Def)
		return;
	
	// Look
	Got = FindDef(0, Def);
	
	// Got it?
	if (Got)
	{
		if (Got->Define)
			free(Got->Define);
		Got->Define = NULL;
		if (Got->Value)
			free(Got->Value);
		Got->Value = NULL;
	}
}

/* IfDef() -- Checks if something is defined by the C Compiler or ourself */
// Return zero if not found
size_t IfDef(const int Type, const char* const Def)
{
	CDefine_t* Got;
	
	// Check
	if (Type < 0 || Type > 1 || !Def)
		return 0;
	
	// Look
	Got = FindDef(0, Def);
	
	// Got it?
	if (Got)
		return (Got - l_Defines[0]) + 1;
	return 0;
}

/* IfNDef() -- Checks if something is not defined by the C Compiler or ourself */
// Return zero if found
size_t IfNDef(const int Type, const char* const Def)
{
	return !IfDef(Type, Def);
}

/* ParseDefines() -- Parses a define in GCC preprocessor output format */
void ParseDefines(FILE* const InDef)
{
#define BUFSIZE 512
	char TempBuf[BUFSIZE];
	char* p;
	char* q;
	
	/* Check */
	if (!InDef)
		return;
	
	/* While we are not at the end of the file */
	while (!feof(InDef))
	{
		// Read in buffer
		fgets(TempBuf, BUFSIZE, InDef);
		
		// Must start with #define (hacky)
		if (strncmp("#define", TempBuf, 7))
			continue;
		
		// remove any newlines
		while ((p = strchr(TempBuf, '\n')))
			*p = 0;
		while ((p = strchr(TempBuf, '\r')))
			*p = 0;
		
		// Find first space
		p = strchr(TempBuf, ' ');
		p++;
		
		// Find first space in that, then NULL it, then +1 it
		q = strchr(p + 1, ' ');
		if (!q)
			continue;	// oops!
		*q = 0;
		q++;
		
		AddDefine(0, p, q);
	}
#undef BUFSIZE
}

/* Eval() -- Evaluates string */
int Eval(const char* const String)
{
#define BUFSIZE 32
	char Buf[BUFSIZE];
	char* Tok;
	char* p, *e;
	int Xor;
	int Result = 0;
	int Op = 0;
	
	// Check
	if (!String)
		return 0;
		
	// While there is a token
	for (Tok = strtok((char* __restrict__)String, " \t"); Tok; Tok = strtok(NULL, " \t")) 
	{
		// Reset
		p = Tok;
		Op = 0;
		
		// If a character is a pipe, only continue if the result is still zero
		if (*p == '|')
		{
			if (Result)
				break;
			continue;
		}
		
		// If character is an ampersand, only continue if result is non-zero
		if (*p == '&')
		{
			// If the result is zero, then ignore
			if (!Result)
				return 0;
			Result = 0;	// Clear result for next run (and)
			continue;
		}
		
		// If the character is a !, the result at the end of opposite
		if (*p == '!')
		{
			p++;
			Op = 1;
		}
		
		// If the first character is a ? check define
		if (*p == '?')
		{
			p++;	// Lose the ?
			Result = !!IfDef(0, p);
		}
		
		// Opposite?
		if (Op)
			Result = !Result;
	}
	
	// Return result
	return Result;
#undef BUFSIZE
}

/* MakeMakefile() -- Creates a makefile */
void MakeMakefile(FILE* const InTemp, FILE* const OutMake)
{
#define BUFSIZE 512
#define MAXDEFINESTACK 64
	char TempBuf[BUFSIZE];
	char IfStates[MAXDEFINESTACK];
	int IfStack;
	char* p, *q, *r;
	size_t i, j, k;
	int Type;
	
	/* Check */
	if (!InTemp || !OutMake)
		return;
	
	/* Force always true state */
	IfStates[0] = 1;
	IfStack = 1;
	
	/* While we are not at the end of the file */
	while (!feof(InTemp))
	{
		// Read in buffer
		fgets(TempBuf, BUFSIZE, InTemp);
		
		// remove any newlines
		while ((p = strchr(TempBuf, '\n')))
			*p = 0;
		while ((p = strchr(TempBuf, '\r')))
			*p = 0;
		
		// What is the first character?
		switch (TempBuf[0])
		{
				// Comment
			case '#':
				// Completely ignore
				break;
				
				// Preprocessor
			case '`':
				// Lose 1
				p = &TempBuf[1];
				
				// Which type is it?
				Type = (p[0] << 8) | p[1];
				
				// Bump up
				p = &p[2];
				
				switch (Type)
				{
					case 0x6966:	// if (if)
						// Is this true or not?
						i = Eval(p);
						
						// Push to the stack
						IfStates[(IfStack++)] = i | (i ? 0x80 : 0);
						break;
						
					case 0x6569:	// ei (else if)
						// Is this true or not?
						i = Eval(p);
						
						// Modify existing stack
						IfStates[IfStack - 1] = ((IfStates[IfStack - 1] & 0x80) ? 0x80 : i | (i ? 0x80 : 0));
						break;
						
					case 0x656c:	// el (else)
						// Modify existing stack
						IfStates[IfStack - 1] = ((IfStates[IfStack - 1] & 0x80) ? 0 : 1 | 0x80);
						break;
						
					case 0x656e:	// en (endif)
						// Lose stack
						IfStack--;
						
						// Too many?
						if (IfStack == 0)
						{
							fprintf(stderr, "Extra `en\n");
							return;
						}
						break;
					
						// Unknown
					default:
						fprintf(stderr, "Unknown PP %x!\n", Type);
						return;
				}
				
				break;
			
				// Normal line
			default:
				// Determine if the stack is completely true
				for (i = 0; i < IfStack; i++)
				{
					if (!(IfStates[i] & 1))
						break;
				}
				
				// Only if it reaches the end is the line parsed
				if (i == IfStack)
					fprintf(OutMake, "%s\n", TempBuf);
				break;
		}
	}

#undef MAXDEFINESTACK
#undef BUFSIZE
}

/* main() -- Main entry point */
int main(int argc, char** argv)
{
	FILE* InDef = NULL;
	FILE* InTemp = NULL;
	FILE* OutMake = NULL;
	
	/* Check command line arguments */
	if (argc < 4)
	{
		fprintf(stderr, "Usage: %s <input> <template> <output>\n", argv[0]);
		fprintf(stderr, "Only %i arguments were passed.\n", argc);
		return EXIT_FAILURE;
	}
	
	/* Attempt opening of arguments */
	// Input C definitions
	InDef = fopen(argv[1], "rt");
	
	// Check
	if (!InDef)
	{
		fprintf(stderr, "Failed to open \"%s\".\n", argv[1]);
		return EXIT_FAILURE;
	}
	
	// Input Template
	InTemp = fopen(argv[2], "rt");
	
	// Check
	if (!InTemp)
	{
		fprintf(stderr, "Failed to open \"%s\".\n", argv[2]);
		fclose(InDef);
		return EXIT_FAILURE;
	}
	
	// Output makefile
	OutMake = fopen(argv[3], "wt");
	
	// Check
	if (!OutMake)
	{
		fprintf(stderr, "Failed to open \"%s\".\n", argv[3]);
		fclose(InDef);
		fclose(InTemp);
		return EXIT_FAILURE;
	}
	
	/* Parse defines then create the makefile */
	ParseDefines(InDef);
	MakeMakefile(InTemp, OutMake);
	
	/* Close and success! */
	fclose(InDef);
	fclose(InTemp);
	fclose(OutMake);
	return EXIT_SUCCESS;
}