#include <stdio.h>
#include <stdlib.h>
#include <glib.h>
#include <gmodule.h>
#include <dlfcn.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
#include "entity.h"
static char *codedir;
static int link_in_so (ENode * node, gchar * lafile);
static void import_in (GModule * module, gchar * function);
static char *stdheaders = " \
#include <stdio.h>\n \
#include <stdlib.h>\n \
#include <glib.h>\n \
#include <entity.h>\n \
";
static GHashTable *c_functions_ht = NULL;
static gint
check_checksum (char *obj, char *code)
{
gchar *checksumfile = g_strconcat (codedir, "/.sum/", obj, NULL);
unsigned int old_hash = 0;
unsigned int new_hash = x31_hash (code);
FILE *file_handle = NULL;
/* if there is a hash, read it in and check agains the new hash */
file_handle = fopen (checksumfile, "r");
if (file_handle) {
fscanf (file_handle, "%u", &old_hash);
fclose (file_handle);
}
g_free (checksumfile);
/* if they are the same ...do nothing but return true */
if (old_hash == new_hash)
return (TRUE);
return (FALSE);
}
static void
write_checksum (char *obj, char *code)
{
gchar *checksumfile = g_strconcat (codedir, "/.sum/", obj, NULL);
unsigned int new_hash = x31_hash (code);
FILE *file_handle = NULL;
file_handle = fopen (checksumfile, "w+");
if (file_handle) {
fprintf (file_handle, "%u", new_hash);
fclose (file_handle);
}
g_free (checksumfile);
}
gchar *
c_compile_str_get (ENode * node, char *tag)
{
char *str = NULL;
char *temp;
GSList *list;
ENode *child;
/* check current node for the tag first */
str = enode_attrib_str (node, tag, NULL);
/* Tag was not found, now check for children */
if (!str) {
for (list = node->children; list; list = list->next) {
child = (ENode *) list->data;
if (strcmp (child->element->str, tag) != 0 || !child->data) {
continue;
}
temp = g_strconcat (child->data->str, str, NULL);
if (str) {
g_free (str);
}
str = temp;
}
}
/* Make sure the string has "something" */
if (!str) {
str = "";
}
return (str);
}
/* Render all the c-code tags. * Code that has been modified will be
* recompiled and relinked into * .so files. These .so are then dlopen'ed
* and functions that have * been exported to entity using the <c-function>
* tag will then * added to the c_functions_ht so that they can be called
* from entity. */
static void
c_node_render (ENode * node)
{
gchar *obj; /* Object name, append .so for the lib name. */
gchar *includes; /* Text inside of <c-includes>. */
gchar *libs; /* Text inside of <c-libs>. */
gchar *obj_cmd; /* Commands that are ran to compile the c. */
gchar *so_cmd;
gchar *lafile; /* Path to the outputed .la library info file
*/
gchar *data;
FILE *fp = NULL;
/* Check to make sure we have a valid node, with data */
if (!node || !node->data) {
return;
}
obj = enode_attrib_str (node, "object", NULL);
if (!obj) {
obj = "libdefault"; /* This will be really SLOW!!! because each
* new */
} /* will have to be compiled. FIXME */
else {
obj = g_strconcat ("lib", obj, NULL); /* libtool needs the object
* to start with a lib */
}
includes = c_compile_str_get (node, "c-includes");
libs = c_compile_str_get (node, "c-libs");
/* This is the data that is checksummed, we need all od this so that *
* the relink and recompile happen if only the <c-[libs|includes] * are
* changed in the xml. */
data = g_strconcat (includes, libs, node->data->str, NULL);
lafile = g_strconcat (codedir, "/", obj, ".la", NULL);
if (check_checksum (obj, data) == FALSE) {
char *tempfile = NULL; /* File created to hold the c code. */
int compile_ok = TRUE;
tempfile = g_strconcat (codedir, "/entity.c", NULL);
/* Open temp file. */
if (tempfile) {
fp = fopen (tempfile, "w");
}
if (!fp) {
g_warning ("Unable to open temp file '%s' for writing: %s",
tempfile, g_strerror (errno));
return;
}
fprintf (fp, "%s", stdheaders);
fprintf (fp, "%s", node->data->str);
fclose (fp);
/* Build the object compile string */
obj_cmd = g_strconcat (DATADIR, "/libtool --mode=compile ", COMPILER, " ",
includes, " `entity-config --cflags` ", tempfile,
" -c -o ",
codedir, "/.objects/", obj, ".lo", NULL);
/* Build the so linking string */
so_cmd = g_strconcat (DATADIR, "/libtool --mode=link ", COMPILER, " ",
libs, " ", "-avoid-version -module ",
codedir, "/.objects/", obj, ".lo",
" -rpath /usr/lib -o ", lafile, NULL);
/* Rebuild if checksum is different */
EDEBUG (("c-embed", "Executing libtool command: %s", obj_cmd));
if (system (obj_cmd)) {
compile_ok = FALSE;
g_warning ("C-code was not recompiled! %s\n", obj_cmd);
} else {
/* on to link stage */
EDEBUG (("c-embed", "Executing libtool command: %s", so_cmd));
if (system (so_cmd)) {
compile_ok = FALSE;
g_warning ("C-code was not relinked! %s\n", so_cmd);
}
}
/* Only write out checksum if the compile was ok */
if (compile_ok == TRUE)
write_checksum (obj, data);
g_free (obj_cmd);
g_free (so_cmd);
g_free (tempfile);
}
g_free (data);
link_in_so (node, lafile);
g_free (lafile);
}
static int
link_in_so (ENode * node, gchar * lafile)
{
GModule *module;
void (*init)(void); /* The init function. */
gchar buf[2048];
FILE *file_handle = NULL;
int err = FALSE;
gchar *modpath;
gchar *dlname;
gchar *command;
gchar **segments = NULL;
dlname = eutils_module_dlname (lafile);
if (!dlname) {
g_warning ("Unable to deduce shared object file to load, giving up!");
return (1);
}
modpath = g_strconcat (codedir, "/.libs/", dlname, NULL);
g_free (dlname);
EDEBUG (("c-embed", "Loading object '%s' from path '%s'", dlname, modpath));
module = g_module_open (modpath, G_MODULE_BIND_LAZY);
if (!module) {
g_warning
("Error loading dynamic library '%s': %s\n",
modpath, g_module_error());
return (1);
}
/* do a popen and hand it a nm command to generate function names */
command = g_strconcat ("nm -p ", modpath, NULL);
EDEBUG (("c-embed", "Executing command: '%s'", command));
file_handle = popen (command, "r");
g_free (command);
g_free (modpath);
/* check the file handle */
if (file_handle) {
while (FALSE == err) {
if (NULL != fgets (buf, 2048, file_handle)) {
/* g_strchomp operates on string in-place */
g_strchomp (buf);
segments = g_strsplit (buf, " ", 0);
if (segments && segments[1] && (0 == strcmp (segments[1], "T")) &&
segments[2] && segments[2] != '\0') {
import_in (module, segments[2]); /* import the module
* from the .so */
}
if (segments)
g_strfreev (segments);
} else {
err = TRUE;
}
}
(void) pclose (file_handle);
}
/* Call the entity_init function in the library if it's there. */
if (g_module_symbol (module, "entity_c_init", (gpointer *) &init) ) {
EDEBUG (("c-embed", "running 'entity_c_init' in C-code.\n"));
enode_call_reference_push (node);
init ();
enode_call_reference_pop ();
}
return (0);
}
/* Imports in a module from a .so */
void
import_in (GModule * module, gchar * function)
{
void (*func) (GSList *);
if (function) {
EDEBUG (("c-embed", "importing in function in '%s'", function));
/* On some systems, all symbols start with _ */
if (function[0] == '_')
function++;
g_module_symbol (module, function, (void **) &func);
if (func) {
g_hash_table_insert (c_functions_ht, g_strdup (function), func);
} else {
g_warning ("nm returned function %s, but theres no such symbol",
function);
}
}
}
EBuf *
c_function_execute (ENode * calling_node, gchar * function, GSList * args)
{
GSList *tmp;
EBuf *retval = NULL;
EBuf *(*funct) (ENode *, GSList *);
funct = g_hash_table_lookup (c_functions_ht, function);
if (funct) {
enode_call_reference_push (calling_node);
retval = funct (calling_node, args);
enode_call_reference_pop ();
} else {
g_warning ("While trying to execute C function %s: %s not defined.\n", function, function);
}
/* Free argument list */
tmp = args;
while (tmp) {
LangArg *arg = tmp->data;
enode_call_free_arg (arg);
tmp = tmp->next;
}
return (retval);
}
#ifdef STATIC_C
void
c_init (RendererFlags flags)
#else
void
renderer_init (RendererFlags flags)
#endif
{
Element *element;
char *sumdir;
char *libdir;
c_functions_ht = g_hash_table_new (g_str_hash, g_str_equal);
if (flags & RENDERER_INIT) {
codedir = g_strconcat (g_get_home_dir (), "/.entity/c-code", NULL);
sumdir = g_strconcat (codedir, "/.sum", NULL);
libdir = g_strconcat (codedir, "/.objects", NULL);
if (mkdir (codedir, 0750) == -1 && errno != EEXIST) {
g_warning ("Cant create %s, no c-code tags can be rendered!\n",
codedir);
}
if (mkdir (sumdir, 0750) == -1 && errno != EEXIST) {
g_warning ("Cant create %s, no c-code tags can be rendered!\n",
sumdir);
}
if (mkdir (libdir, 0750) == -1 && errno != EEXIST) {
g_warning ("Cant create %s, no c-code tags can be rendered!\n",
libdir);
}
g_free (sumdir);
g_free (libdir);
}
if (flags & RENDERER_REGISTER) {
element = g_new0 (Element, 1);
element->render_func = c_node_render;
element->destroy_func = NULL;
element->description =
"Embed C code directly into an Entity application.";
element->tag = "c-code";
element_register (element);
language_register ("c-code", c_function_execute);
language_register ("C", c_function_execute);
language_register ("c", c_function_execute);
}
}
syntax highlighted by Code2HTML, v. 0.9.1