#define CHAZ_USE_SHORT_NAMES
#include "Charmonizer/Core/HeaderChecker.h"
#include "Charmonizer/Core/Compiler.h"
#include "Charmonizer/Core/ConfWriter.h"
#include "Charmonizer/Core/Util.h"
#include <string.h>
#include <stdlib.h>
typedef struct Header {
char *name;
chaz_bool_t exists;
} Header;
/* "hello_world.c" without the hello or the world. */
static char test_code[] = "int main() { return 0; }\n";
/* Keep a sorted, dynamically-sized array of names of all headers we've
* checked for so far.
*/
static int cache_size = 0;
static Header **header_cache = NULL;
/* Comparison function to feed to qsort, bsearch, etc.
*/
static int
S_compare_headers(const void *vptr_a, const void *vptr_b);
/* Run a test compilation and return a new Header object encapsulating the
* results.
*/
static Header*
S_discover_header(const char *header_name);
/* Extend the cache, add this Header object to it, and sort.
*/
static void
S_add_to_cache(Header *header);
/* Like add_to_cache, but takes a individual elements instead of a Header* and
* checks if header exists in array first.
*/
static void
S_maybe_add_to_cache(const char *header_name, chaz_bool_t exists);
void
HeadCheck_init()
{
Header *null_header = (Header*)malloc(sizeof(Header));
/* Create terminating record for the dynamic array of Header objects. */
null_header->name = NULL;
null_header->exists = false;
header_cache = (Header**)malloc(sizeof(void*));
*header_cache = null_header;
cache_size = 1;
}
chaz_bool_t
HeadCheck_check_header(const char *header_name)
{
Header *header;
Header key;
Header *fake = &key;
Header **header_ptr;
/* Fake up a key to feed to bsearch; see if the header's already there. */
key.name = (char*)header_name;
key.exists = false;
header_ptr = (Header**)bsearch(&fake, header_cache, cache_size,
sizeof(void*), S_compare_headers);
/* If it's not there, go try a test compile. */
if (header_ptr == NULL) {
header = S_discover_header(header_name);
S_add_to_cache(header);
}
else {
header = *header_ptr;
}
return header->exists;
}
chaz_bool_t
HeadCheck_check_many_headers(const char **header_names)
{
chaz_bool_t success;
int i;
char *code_buf = Util_strdup("");
size_t needed = sizeof(test_code) + 20;
/* Build the source code string. */
for (i = 0; header_names[i] != NULL; i++) {
needed += strlen(header_names[i]);
needed += sizeof("#include <>\n");
}
code_buf = (char*)malloc(needed);
code_buf[0] = '\0';
for (i = 0; header_names[i] != NULL; i++) {
strcat(code_buf, "#include <");
strcat(code_buf, header_names[i]);
strcat(code_buf, ">\n");
}
strcat(code_buf, test_code);
/* If the code compiles, bulk add all header names to the cache. */
success = CC_test_compile(code_buf, strlen(code_buf));
if (success) {
for (i = 0; header_names[i] != NULL; i++) {
S_maybe_add_to_cache(header_names[i], true);
}
}
free(code_buf);
return success;
}
static char contains_code[] =
QUOTE( #include <stddef.h> )
QUOTE( %s )
QUOTE( int main() { return offsetof(%s, %s); } );
chaz_bool_t
HeadCheck_contains_member(const char *struct_name, const char *member,
const char *includes)
{
long needed = sizeof(contains_code) + strlen(struct_name)
+ strlen(member) + strlen(includes) + 10;
char *buf = (char*)malloc(needed);
chaz_bool_t retval;
sprintf(buf, contains_code, includes, struct_name, member);
retval = CC_test_compile(buf, strlen(buf));
free(buf);
return retval;
}
static int
S_compare_headers(const void *vptr_a, const void *vptr_b) {
Header **const a = (Header**)vptr_a;
Header **const b = (Header**)vptr_b;
/* (NULL is "greater than" any string.) */
if ((*a)->name == NULL) { return 1; }
else if ((*b)->name == NULL) { return -1; }
else { return strcmp((*a)->name, (*b)->name); }
}
static Header*
S_discover_header(const char *header_name)
{
Header* header = (Header*)malloc(sizeof(Header));
size_t needed = strlen(header_name) + sizeof(test_code) + 50;
char *include_test = (char*)malloc(needed);
/* Assign. */
header->name = Util_strdup(header_name);
/* See whether code that tries to pull in this header compiles. */
sprintf(include_test, "#include <%s>\n%s", header_name, test_code);
header->exists = CC_test_compile(include_test, strlen(include_test));
free(include_test);
return header;
}
static void
S_add_to_cache(Header *header)
{
/* Realloc array -- inefficient, but this isn't a bottleneck. */
cache_size++;
header_cache = (Header**)realloc(header_cache, (cache_size * sizeof(void*)));
header_cache[ cache_size - 1 ] = header;
/* Keep the list of headers sorted. */
qsort(header_cache, cache_size, sizeof(*header_cache), S_compare_headers);
}
static void
S_maybe_add_to_cache(const char *header_name, chaz_bool_t exists)
{
Header *header;
Header key;
Header *fake = &key;
/* Fake up a key and bsearch for it. */
key.name = (char*)header_name;
key.exists = exists;
header = (Header*)bsearch(&fake, header_cache, cache_size,
sizeof(void*), S_compare_headers);
/* We've already done the test compile, so skip that step and add it. */
if (header == NULL) {
header = (Header*)malloc(sizeof(Header));
header->name = Util_strdup(header_name);
header->exists = exists;
S_add_to_cache(header);
}
}
/* Copyright 2006-2011 Marvin Humphrey
*
* This program is free software; you can redistribute it and/or modify
* under the same terms as Perl itself.
*/