#define CHAZ_USE_SHORT_NAMES
#include "Charmonizer/Core/Util.h"
#include "Charmonizer/Core/ModHandler.h"
#include "Charmonizer/Core/OperSys.h"
#include "Charmonizer/Core/Compiler.h"
#include <errno.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
/* Write the "_charm.h" file used by every probe.
*/
static void
write_charm_h();
/* Compile a small wrapper application which is used to redirect error output
* to devnull.
*/
static void
build_charm_run();
/* Clean up the files associated with capture_output().
*/
static void
clean_up_try();
/* global vars */
struct chaz_OperSys *chaz_ModHand_os = NULL;
struct chaz_Compiler *chaz_ModHand_compiler = NULL;
chaz_bool_t chaz_ModHand_charm_run_available = false;
FILE* chaz_ModHand_charmony_fh = NULL;
/* static vars */
static char *try_app_path = NULL;
static char *try_app_command = NULL;
void
chaz_ModHand_init()
{
/* set the name of the application which we "try" to execute */
(void)join_strings(&try_app_path, 0, TRY_APP_BASENAME, os->exe_ext, NULL);
/* set the invocation string for the "try" application */
(void)join_strings(&try_app_command, 0, os->local_command_start,
try_app_path, NULL);
/* write files needed by this module and others */
build_charm_run();
write_charm_h();
}
void
chaz_ModHand_open_charmony_h(const char *charmony_start)
{
/* open the filehandle */
charmony_fh = fopen("charmony.h", "w+");
if (charmony_fh == NULL)
die("Can't open 'charmony.h': %s", strerror(errno));
/* print supplied text (if any) along with warning, open include guard */
if (charmony_start != NULL)
fprintf(charmony_fh, charmony_start);
fprintf(charmony_fh,
"/* Header file auto-generated by Charmonizer. \n"
" * DO NOT EDIT THIS FILE!!\n"
" */\n\n"
"#ifndef H_CHARMONY\n"
"#define H_CHARMONY 1\n\n"
);
}
void
chaz_ModHand_clean_up(void)
{
/* clean up some temp files */
remove("_charm.h");
os->remove_exe(os, "_charm_run");
os->remove_exe(os, "_charm_stat");
/* write the last bit of charmony.h and close */
fprintf(charmony_fh, "#endif /* H_CHARMONY */\n\n");
if (fclose(charmony_fh))
die("Couldn't close 'charmony.h': %s", strerror(errno));
}
static char charm_h_code[] = "\n"
" #ifndef CHARM_H\n"
" #define CHARM_H 1\n"
"\n"
" #include <stdio.h>\n"
"\n"
" #define Charm_Setup \\\n"
" freopen(\"_charmonizer_target\", \"w\", stdout)\n"
" \n"
" #endif\n"
"";
static void
write_charm_h()
{
write_file("_charm.h", charm_h_code, strlen(charm_h_code));
}
static char charm_run_code_a[] = "\n"
" #include <stdio.h>\n"
" #include <stdlib.h>\n"
" #include <string.h>\n"
" #include <stddef.h>\n"
" int main(int argc, char **argv)\n"
" {\n"
" char *command;\n"
" size_t command_len = 1; /* terminating null */\n"
" int i;\n"
" int retval;\n"
" \n"
" /* rebuild the command line args, minus the name of this utility */\n"
" for (i = 1; i < argc; i++) {\n"
" command_len += strlen(argv[i]) + 1;\n"
" }\n"
" command = (char*)calloc(command_len, sizeof(char));\n"
"";
static char charm_run_code_b[] = "\n"
" if (command == NULL) {\n"
" fprintf(stderr, \"calloc failed\\n\");\n"
" exit(1);\n"
" }\n"
" for (i = 1; i < argc; i++) {\n"
" strcat( strcat(command, \" \"), argv[i] );\n"
" }\n"
"\n"
" /* redirect stdout and stderr to /dev/null or equivalent */\n"
" freopen( \n"
"";
static char charm_run_code_c[] = "\n"
" , \"w\", stdout);\n"
" freopen( \n"
"";
static char charm_run_code_d[] = "\n"
" , \"w\", stderr);\n"
"\n"
" /* run the commmand and return its value to the parent process */\n"
" retval = system(command);\n"
" free(command);\n"
" return retval;\n"
" }\n"
"";
static void
build_charm_run()
{
char *code = NULL;
chaz_bool_t compile_succeeded = false;
(void)join_strings(&code, 0, charm_run_code_a, charm_run_code_b, " \"",
os->devnull, "\" ", charm_run_code_c, " \"", os->devnull, "\" ",
charm_run_code_d, NULL);
compile_succeeded = compiler->compile_exe(compiler, "_charm_run.c",
"_charm_run", code, strlen(code));
if (!compile_succeeded)
die("failed to compile _charm_run helper utility");
remove("_charm_run.c");
free(code);
charm_run_available = true;
}
chaz_bool_t
chaz_ModHand_test_compile(char *source, size_t source_len)
{
chaz_bool_t compile_succeeded;
if ( !remove_and_verify(try_app_path) )
die("Failed to delete file '%s'", try_app_path);
compile_succeeded = compiler->compile_exe(compiler, TRY_SOURCE_PATH,
TRY_APP_BASENAME, source, source_len);
clean_up_try();
return compile_succeeded;
}
char*
chaz_ModHand_capture_output(char *source, size_t source_len,
size_t *output_len)
{
char *captured_output = NULL;
chaz_bool_t compile_succeeded;
/* clear out previous versions and test to make sure removal worked */
if ( !remove_and_verify(try_app_path) )
die("Failed to delete file '%s'", try_app_path);
if ( !remove_and_verify(TARGET_PATH) )
die("Failed to delete file '%s'", TARGET_PATH);
/* attempt compilation; if successful, run app and slurp output */
compile_succeeded = compiler->compile_exe(compiler, TRY_SOURCE_PATH,
TRY_APP_BASENAME, source, source_len);
if (compile_succeeded) {
system(try_app_command);
captured_output = slurp_file(TARGET_PATH, output_len);
}
else {
*output_len = 0;
}
/* remove all the files we just created */
clean_up_try();
return captured_output;
}
void
chaz_ModHand_append_conf(char *fmt, ...)
{
va_list args;
va_start(args, fmt);
vfprintf(chaz_ModHand_charmony_fh, fmt, args);
va_end(args);
}
static void
clean_up_try()
{
remove(TRY_SOURCE_PATH);
os->remove_exe(os, TRY_APP_BASENAME);
remove(TARGET_PATH);
}
void
chaz_ModHand_shorten_macro(const char *sym)
{
append_conf(" #define %s CHY_%s\n", sym, sym);
}
void
chaz_ModHand_shorten_typedef(const char *sym)
{
append_conf(" #define %s chy_%s\n", sym, sym);
}
void
chaz_ModHand_shorten_function(const char *sym)
{
append_conf(" #define %s chy_%s\n", sym, sym);
}
/* Copyright 2006-2007 Marvin Humphrey
*
* This program is free software; you can redistribute it and/or modify
* under the same terms as Perl itself.
*/