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;
}