/* This is an auto-generated file -- do not edit directly. */

/* Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/***************************************************************************/

#line 21 "src/Charmonizer/Core/Defines.h"
/* Charmonizer/Core/Defines.h -- Universal definitions.
 */
#ifndef H_CHAZ_DEFINES
#define H_CHAZ_DEFINES 1

#ifndef true
  #define true 1
  #define false 0
#endif

#define CHAZ_QUOTE(x) #x "\n"

#endif /* H_CHAZ_DEFINES */


/***************************************************************************/

#line 21 "src/Charmonizer/Core/CFlags.h"
/* Charmonizer/Core/CFlags.h
 */

#ifndef H_CHAZ_CFLAGS
#define H_CHAZ_CFLAGS

#define CHAZ_CFLAGS_STYLE_POSIX  1
#define CHAZ_CFLAGS_STYLE_GNU    2
#define CHAZ_CFLAGS_STYLE_MSVC   3
#define CHAZ_CFLAGS_STYLE_SUN_C  4

typedef struct chaz_CFlags chaz_CFlags;

chaz_CFlags*
chaz_CFlags_new(int style);

void
chaz_CFlags_destroy(chaz_CFlags *flags);

const char*
chaz_CFlags_get_string(chaz_CFlags *flags);

void
chaz_CFlags_append(chaz_CFlags *flags, const char *string);

void
chaz_CFlags_clear(chaz_CFlags *flags);

void
chaz_CFlags_set_output_obj(chaz_CFlags *flags, const char *filename);

void
chaz_CFlags_set_output_exe(chaz_CFlags *flags, const char *filename);

void
chaz_CFlags_add_define(chaz_CFlags *flags, const char *name,
                       const char *value);

void
chaz_CFlags_add_include_dir(chaz_CFlags *flags, const char *dir);

void
chaz_CFlags_enable_optimization(chaz_CFlags *flags);

void
chaz_CFlags_disable_strict_aliasing(chaz_CFlags *flags);

void
chaz_CFlags_set_warnings_as_errors(chaz_CFlags *flags);

void
chaz_CFlags_compile_shared_library(chaz_CFlags *flags);

void
chaz_CFlags_hide_symbols(chaz_CFlags *flags);

void
chaz_CFlags_link_shared_library(chaz_CFlags *flags, const char *basename,
                                const char *version,
                                const char *major_version);

void
chaz_CFlags_set_link_output(chaz_CFlags *flags, const char *filename);

void
chaz_CFlags_add_library_path(chaz_CFlags *flags, const char *directory);

void
chaz_CFlags_add_shared_lib(chaz_CFlags *flags, const char *dir,
                           const char *basename, const char *major_version);

void
chaz_CFlags_add_external_lib(chaz_CFlags *flags, const char *library);

void
chaz_CFlags_add_rpath(chaz_CFlags *flags, const char *path);

void
chaz_CFlags_enable_code_coverage(chaz_CFlags *flags);

#endif /* H_CHAZ_CFLAGS */



/***************************************************************************/

#line 21 "src/Charmonizer/Core/CLI.h"
#ifndef H_CHAZ_CLI
#define H_CHAZ_CLI 1

#define CHAZ_CLI_NO_ARG       0
#define CHAZ_CLI_ARG_REQUIRED (1 << 0)
#define CHAZ_CLI_ARG_OPTIONAL (1 << 1)

/* The CLI module provides argument parsing for a command line interface.
 */

typedef struct chaz_CLI chaz_CLI;

/* Constructor.
 *
 * @param name The name of the application.
 * @param description A description of the application.
 */
chaz_CLI*
chaz_CLI_new(const char *name, const char *description);

/* Destructor.
 */
void
chaz_CLI_destroy(chaz_CLI *self);

/* Return a string combining usage header with documentation of options.
 */
const char*
chaz_CLI_help(chaz_CLI *self);

/* Override the generated usage header.
 */
void
chaz_CLI_set_usage(chaz_CLI *self, const char *usage);

/* Register an option.  Updates the "help" string, invalidating previous
 * values.  Returns true on success, or reports an error and returns false if
 * the option was already registered.
 */
int
chaz_CLI_register(chaz_CLI *self, const char *name, const char *help,
                  int flags);

/* Set an option.  The specified option must have been registered previously.
 * The supplied `value` is optional and will be copied.
 *
 * Returns true on success.  Reports an error and returns false on failure.
 */
int
chaz_CLI_set(chaz_CLI *self, const char *name, const char *value);

/* Returns true if the option has been set, false otherwise.
 */
int
chaz_CLI_defined(chaz_CLI *self, const char *name);

/* Return the value of a given option converted to a long int.  Defaults to 0.
 * Reports an error if the named option has not been registered.
 */
long
chaz_CLI_longval(chaz_CLI *self, const char *name);

/* Return the value of an option as a C string.  Defaults to NULL.  Reports an
 * error if the named option has not been registered.
 */
const char*
chaz_CLI_strval(chaz_CLI *self, const char *name);

/* Unset an option, making subsequent calls to `get` return false and making
 * it possible to call `set` again.
 *
 * Returns true if the option exists and was able to be unset.
 */
int
chaz_CLI_unset(chaz_CLI *self, const char *name);

/* Parse `argc` and `argv`, setting options as appropriate.  Returns true on
 * success.  Reports an error and returns false if either an unexpected option
 * was encountered or an option which requires an argument was supplied
 * without one.
 */
int
chaz_CLI_parse(chaz_CLI *self, int argc, const char *argv[]);

#endif /* H_CHAZ_CLI */



/***************************************************************************/

#line 21 "src/Charmonizer/Core/Compiler.h"
/* Charmonizer/Core/Compiler.h
 */

#ifndef H_CHAZ_COMPILER
#define H_CHAZ_COMPILER

#include <stddef.h>
/* #include "Charmonizer/Core/Defines.h" */
/* #include "Charmonizer/Core/CFlags.h" */

#define CHAZ_CC_BINFMT_ELF      1
#define CHAZ_CC_BINFMT_MACHO    2
#define CHAZ_CC_BINFMT_PE       3

/* Attempt to compile and link an executable.  Return true if the executable
 * file exists after the attempt.
 */
int
chaz_CC_compile_exe(const char *source_path, const char *exe_path,
                    const char *code);

/* Attempt to compile an object file.  Return true if the object file
 * exists after the attempt.
 */
int
chaz_CC_compile_obj(const char *source_path, const char *obj_path,
                    const char *code);

/* Attempt to compile the supplied source code and return true if the
 * effort succeeds.
 */
int
chaz_CC_test_compile(const char *source);

/* Attempt to compile and link the supplied source code and return true if
 * the effort succeeds.
 */
int
chaz_CC_test_link(const char *source);

/* Attempt to compile the supplied source code.  If successful, capture the
 * output of the program and return a pointer to a newly allocated buffer.
 * If the compilation fails, return NULL.  The length of the captured
 * output will be placed into the integer pointed to by [output_len].
 */
char*
chaz_CC_capture_output(const char *source, size_t *output_len);

/** Return true if macro is defined.
 */
int
chaz_CC_has_macro(const char *macro);

/** Initialize the compiler environment.
 */
void
chaz_CC_init(const char *cc_command, const char *cflags);

/* Clean up the environment.
 */
void
chaz_CC_clean_up(void);

/* Accessor for the compiler executable's string representation.
 */
const char*
chaz_CC_get_cc(void);

/* Accessor for `cflags`.
 */
const char*
chaz_CC_get_cflags(void);

/* Accessor for `extra_cflags`.
 */
chaz_CFlags*
chaz_CC_get_extra_cflags(void);

/* Accessor for `temp_cflags`.
 */
chaz_CFlags*
chaz_CC_get_temp_cflags(void);

/* Return a new CFlags object.
 */
chaz_CFlags*
chaz_CC_new_cflags(void);

/* Return the binary format.
 */
int
chaz_CC_binary_format(void);

/* Return the extension for an executable.
 */
const char*
chaz_CC_exe_ext(void);

/* Return the extension for a shared (dynamic) library.
 */
const char*
chaz_CC_shared_lib_ext(void);

/* Return the extension for a static library.
 */
const char*
chaz_CC_static_lib_ext(void);

/* Return the extension for an import library (Windows).
 */
const char*
chaz_CC_import_lib_ext(void);

/* Return the extension for a compiled object.
 */
const char*
chaz_CC_obj_ext(void);

int
chaz_CC_gcc_version_num(void);

const char*
chaz_CC_gcc_version(void);

int
chaz_CC_msvc_version_num(void);

int
chaz_CC_sun_c_version_num(void);

int
chaz_CC_is_cygwin(void);

int
chaz_CC_is_mingw(void);

const char*
chaz_CC_link_command(void);

/* Create a command for building a static library.
 *
 * @param target The target library filename.
 * @param objects The list of object files to be archived in the library.
 */
char*
chaz_CC_format_archiver_command(const char *target, const char *objects);

/* Returns a "ranlib" command if valid.
 *
 * @param target The library filename.
 */
char*
chaz_CC_format_ranlib_command(const char *target);

/** Returns the filename for a shared library.
 *
 * @param dir The target directory or NULL for the current directory.
 * @param basename The name of the library without prefix and extension.
 * @param version The library version.
 */
char*
chaz_CC_shared_lib_filename(const char *dir, const char *basename,
                            const char *version);

/** Returns the filename for an import library.
 *
 * @param dir The target directory or NULL for the current directory.
 * @param basename The name of the library without prefix and extension.
 * @param version The library version.
 */
char*
chaz_CC_import_lib_filename(const char *dir, const char *basename,
                            const char *version);

/** Returns the filename for an MSVC export file.
 *
 * @param dir The target directory or NULL for the current directory.
 * @param basename The name of the library without prefix and extension.
 * @param version The library version.
 */
char*
chaz_CC_export_filename(const char *dir, const char *basename,
                        const char *version);

/** Returns the filename for a static library.
 *
 * @param dir The target directory or NULL for the current directory.
 * @param basename The name of the library without prefix and extension.
 */
char*
chaz_CC_static_lib_filename(const char *dir, const char *basename);

#endif /* H_CHAZ_COMPILER */



/***************************************************************************/

#line 21 "src/Charmonizer/Core/ConfWriter.h"
/* Charmonizer/Core/ConfWriter.h -- Write to a config file.
 */

#ifndef H_CHAZ_CONFWRITER
#define H_CHAZ_CONFWRITER 1

#include <stddef.h>
#include <stdarg.h>
/* #include "Charmonizer/Core/Defines.h" */

struct chaz_ConfWriter;

/* Initialize elements needed by ConfWriter.  Must be called before anything
 * else, but after os and compiler are initialized.
 */
void
chaz_ConfWriter_init(void);

/* Close the include guard on charmony.h, then close the file.  Delete temp
 * files and perform any other needed cleanup.
 */
void
chaz_ConfWriter_clean_up(void);

/* Print output to charmony.h.
 */
void
chaz_ConfWriter_append_conf(const char *fmt, ...);

/* Add a pound-define.
 */
void
chaz_ConfWriter_add_def(const char *sym, const char *value);

/* Add a globally scoped pound-define.
 */
void
chaz_ConfWriter_add_global_def(const char *sym, const char *value);

/* Add a typedef.
 */
void
chaz_ConfWriter_add_typedef(const char *type, const char *alias);

/* Add a globally scoped typedef.
 */
void
chaz_ConfWriter_add_global_typedef(const char *type, const char *alias);

/* Pound-include a system header (within angle brackets).
 */
void
chaz_ConfWriter_add_sys_include(const char *header);

/* Pound-include a locally created header (within quotes).
 */
void
chaz_ConfWriter_add_local_include(const char *header);

/* Print a "chapter heading" comment in the conf file when starting a module.
 */
void
chaz_ConfWriter_start_module(const char *module_name);

/* Leave a little whitespace at the end of each module.
 */
void
chaz_ConfWriter_end_module(void);

void
chaz_ConfWriter_add_writer(struct chaz_ConfWriter *writer);

typedef void
(*chaz_ConfWriter_clean_up_t)(void);
typedef void
(*chaz_ConfWriter_vappend_conf_t)(const char *fmt, va_list args); 
typedef void
(*chaz_ConfWriter_add_def_t)(const char *sym, const char *value);
typedef void
(*chaz_ConfWriter_add_global_def_t)(const char *sym, const char *value);
typedef void
(*chaz_ConfWriter_add_typedef_t)(const char *type, const char *alias);
typedef void
(*chaz_ConfWriter_add_global_typedef_t)(const char *type, const char *alias);
typedef void
(*chaz_ConfWriter_add_sys_include_t)(const char *header);
typedef void
(*chaz_ConfWriter_add_local_include_t)(const char *header);
typedef void
(*chaz_ConfWriter_start_module_t)(const char *module_name);
typedef void
(*chaz_ConfWriter_end_module_t)(void);
typedef struct chaz_ConfWriter {
    chaz_ConfWriter_clean_up_t           clean_up;
    chaz_ConfWriter_vappend_conf_t       vappend_conf;
    chaz_ConfWriter_add_def_t            add_def;
    chaz_ConfWriter_add_global_def_t     add_global_def;
    chaz_ConfWriter_add_typedef_t        add_typedef;
    chaz_ConfWriter_add_global_typedef_t add_global_typedef;
    chaz_ConfWriter_add_sys_include_t    add_sys_include;
    chaz_ConfWriter_add_local_include_t  add_local_include;
    chaz_ConfWriter_start_module_t       start_module;
    chaz_ConfWriter_end_module_t         end_module;
} chaz_ConfWriter;

#endif /* H_CHAZ_CONFWRITER */



/***************************************************************************/

#line 21 "src/Charmonizer/Core/ConfWriterC.h"
/* Charmonizer/Core/ConfWriterC.h -- Write to a C header file.
 */

#ifndef H_CHAZ_CONFWRITERC
#define H_CHAZ_CONFWRITERC 1

/* Enable writing config to a C header file.
 */
void
chaz_ConfWriterC_enable(void);

#endif /* H_CHAZ_CONFWRITERC */



/***************************************************************************/

#line 21 "src/Charmonizer/Core/ConfWriterPerl.h"
/* Charmonizer/Core/ConfWriterPerl.h -- Write to a Perl module file.
 */

#ifndef H_CHAZ_CONFWRITERPERL
#define H_CHAZ_CONFWRITERPERL 1

/* Enable writing config to a Perl module file.
 */
void
chaz_ConfWriterPerl_enable(void);

#endif /* H_CHAZ_CONFWRITERPERL */


/***************************************************************************/

#line 21 "src/Charmonizer/Core/ConfWriterPython.h"
/* Charmonizer/Core/ConfWriterPython.h -- Write to a Python module file.
 */

#ifndef H_CHAZ_CONFWRITERPYTHON
#define H_CHAZ_CONFWRITERPYTHON 1

/* Enable writing config to a Python module file.
 */
void
chaz_ConfWriterPython_enable(void);

#endif /* H_CHAZ_CONFWRITERPYTHON */


/***************************************************************************/

#line 21 "src/Charmonizer/Core/ConfWriterRuby.h"
/* Charmonizer/Core/ConfWriterRuby.h -- Write to a Ruby module file.
 */

#ifndef H_CHAZ_CONFWRITERRUBY
#define H_CHAZ_CONFWRITERRUBY 1

/* Enable writing config to a Ruby module file.
 */
void
chaz_ConfWriterRuby_enable(void);

#endif /* H_CHAZ_CONFWRITERRUBY */


/***************************************************************************/

#line 21 "src/Charmonizer/Core/HeaderChecker.h"
/* Charmonizer/Probe/HeaderChecker.h
 */

#ifndef H_CHAZ_HEAD_CHECK
#define H_CHAZ_HEAD_CHECK

/* #include "Charmonizer/Core/Defines.h" */

/* Bootstrap the HeadCheck.  Call this before anything else.
 */
void
chaz_HeadCheck_init(void);

/* Check for a particular header and return true if it's available.  The
 * test-compile is only run the first time a given request is made.
 */
int
chaz_HeadCheck_check_header(const char *header_name);

/* Attempt to compile a file which pulls in all the headers specified by name
 * in a null-terminated array.  If the compile succeeds, add them all to the
 * internal register and return true.
 */
int
chaz_HeadCheck_check_many_headers(const char **header_names);

/* Return true if the symbol is defined (possibly as a macro). */
int
chaz_HeadCheck_defines_symbol(const char *symbol, const char *includes);

/* Return true if the member is present in the struct. */
int
chaz_HeadCheck_contains_member(const char *struct_name, const char *member,
                               const char *includes);

#endif /* H_CHAZ_HEAD_CHECK */



/***************************************************************************/

#line 21 "src/Charmonizer/Core/Make.h"
/* Charmonizer/Core/Make.h
 */

#ifndef H_CHAZ_MAKE
#define H_CHAZ_MAKE

/* #include "Charmonizer/Core/CFlags.h" */
/* #include "Charmonizer/Core/CLI.h" */

typedef struct chaz_MakeFile chaz_MakeFile;
typedef struct chaz_MakeVar chaz_MakeVar;
typedef struct chaz_MakeRule chaz_MakeRule;
typedef struct chaz_MakeBinary chaz_MakeBinary;

typedef void
(*chaz_Make_file_callback_t)(const char *dir, char *file, void *context);
typedef int
(*chaz_Make_file_filter_t)(const char *dir, char *file, void *context);

/** Initialize the environment.
 *
 * @param make_command Name of the make command. Auto-detect if NULL.
 */
void
chaz_Make_init(const char *make_command);

/** Clean up the environment.
 */
void
chaz_Make_clean_up(void);

/** Return the name of the detected 'make' executable.
 */
const char*
chaz_Make_get_make(void);

/** Return the type of shell used by the detected 'make' executable.
 */
int
chaz_Make_shell_type(void);

/** Recursively list files in a directory. For every file a callback is called
 * with the filename and a context variable.
 *
 * @param dir Directory to search in.
 * @param ext File extension to search for.
 * @param callback Callback to call for every matching file.
 * @param context Context variable to pass to callback.
 */
void
chaz_Make_list_files(const char *dir, const char *ext,
                     chaz_Make_file_callback_t callback, void *context);

/** MakeFile constructor.
 */
chaz_MakeFile*
chaz_MakeFile_new();

/** MakeFile destructor.
 */
void
chaz_MakeFile_destroy(chaz_MakeFile *self);

/** Add a variable to a makefile.
 *
 * @param name Name of the variable.
 * @param value Value of the variable. Can be NULL if you want add content
 * later.
 * @return a MakeVar.
 */
chaz_MakeVar*
chaz_MakeFile_add_var(chaz_MakeFile *self, const char *name,
                      const char *value);

/** Add a rule to a makefile.
 *
 * @param target The first target of the rule. Can be NULL if you want to add
 * targets later.
 * @param prereq The first prerequisite of the rule. Can be NULL if you want to
 * add prerequisites later.
 * @return a MakeRule.
 */
chaz_MakeRule*
chaz_MakeFile_add_rule(chaz_MakeFile *self, const char *target,
                       const char *prereq);

/** Return the rule for the 'clean' target.
 */
chaz_MakeRule*
chaz_MakeFile_clean_rule(chaz_MakeFile *self);

/** Return the rule for the 'distclean' target.
 */
chaz_MakeRule*
chaz_MakeFile_distclean_rule(chaz_MakeFile *self);

/** Add an executable. Returns a chaz_MakeBinary object.
 *
 * @param dir The target directory or NULL for the current directory.
 * @param basename The name of the executable without extension.
 */
chaz_MakeBinary*
chaz_MakeFile_add_exe(chaz_MakeFile *self, const char *dir,
                      const char *basename);

/** Add a shared library. The library will be built in the current directory.
 * Returns a chaz_MakeBinary object.
 *
 * @param dir The target directory or NULL for the current directory.
 * @param basename The name of the library without prefix and extension.
 * @param version The version of the library.
 * @param major_version The major version of the library.
 */
chaz_MakeBinary*
chaz_MakeFile_add_shared_lib(chaz_MakeFile *self, const char *dir,
                             const char *basename, const char *version,
                             const char *major_version);

/** Add a static library. The library will be built in the current directory.
 * Returns a chaz_MakeBinary object.
 *
 * @param dir The target directory or NULL for the current directory.
 * @param basename The name of the library without prefix and extension.
 */
chaz_MakeBinary*
chaz_MakeFile_add_static_lib(chaz_MakeFile *self, const char *dir,
                             const char *basename);

/** Add a rule to build the lemon parser generator.
 *
 * @param dir The lemon directory.
 */
chaz_MakeBinary*
chaz_MakeFile_add_lemon_exe(chaz_MakeFile *self, const char *dir);

/** Add a rule for a lemon grammar.
 *
 * @param base_name The filename of the grammar without extension.
 */
chaz_MakeRule*
chaz_MakeFile_add_lemon_grammar(chaz_MakeFile *self, const char *base_name);

/** Write the makefile to a file named 'Makefile' in the current directory.
 */
void
chaz_MakeFile_write(chaz_MakeFile *self);

/** Append content to a makefile variable. The new content will be separated
 * from the existing content with whitespace.
 *
 * @param element The additional content.
 */
void
chaz_MakeVar_append(chaz_MakeVar *self, const char *element);

/** Add another target to a makefile rule.
 *
 * @param target The additional rule.
 */
void
chaz_MakeRule_add_target(chaz_MakeRule *self, const char *target);

/** Add another prerequisite to a makefile rule.
 *
 * @param prereq The additional prerequisite.
 */
void
chaz_MakeRule_add_prereq(chaz_MakeRule *self, const char *prereq);

/** Add a command to a rule.
 *
 * @param command The additional command.
 */
void
chaz_MakeRule_add_command(chaz_MakeRule *self, const char *command);

/** Add a command to remove one or more files.
 *
 * @param files The list of files.
 */
void
chaz_MakeRule_add_rm_command(chaz_MakeRule *self, const char *files);

/** Add a command to remove one or more directories.
 *
 * @param dirs The list of directories.
 */
void
chaz_MakeRule_add_recursive_rm_command(chaz_MakeRule *self, const char *dirs);

/** Add one or more commands to call another makefile recursively.
 *
 * @param dir The directory in which to call the makefile.
 * @param target The target to call. Pass NULL for the default target.
 */
void
chaz_MakeRule_add_make_command(chaz_MakeRule *self, const char *dir,
                               const char *target);

/** Add a source file for the binary.
 *
 * @param dir The source directory or NULL for the current directory.
 * @param filename The filename.
 */
void
chaz_MakeBinary_add_src_file(chaz_MakeBinary *self, const char *dir,
                             const char *filename);

/** Add all .c files in a directory as sources for the binary.
 *
 * @param path The path to the directory.
 */
void
chaz_MakeBinary_add_src_dir(chaz_MakeBinary *self, const char *path);

/** Add .c files in a directory as sources for the binary if they match
 * a filter.
 *
 * @param path The path to the directory.
 * @param filter A callback that is invoked for every source file. The
 * source file is only added if the callback returns true. May be NULL.
 * @param context Context passed to filter.
 */
void
chaz_MakeBinary_add_filtered_src_dir(chaz_MakeBinary *self, const char *path,
                                     chaz_Make_file_filter_t filter,
                                     void *context);

/** Add a prerequisite to the make rule of the binary.
 *
 * @param prereq The prerequisite.
 */
void
chaz_MakeBinary_add_prereq(chaz_MakeBinary *self, const char *prereq);

/** Return a list of all objects separated by space.
 */
char*
chaz_MakeBinary_obj_string(chaz_MakeBinary *self);

/** Accessor for target.
 */
const char*
chaz_MakeBinary_get_target(chaz_MakeBinary *self);

/** Accessor for compile flags.
 */
chaz_CFlags*
chaz_MakeBinary_get_compile_flags(chaz_MakeBinary *self);

/** Accessor for link flags.
 */
chaz_CFlags*
chaz_MakeBinary_get_link_flags(chaz_MakeBinary *self);

#endif /* H_CHAZ_MAKE */



/***************************************************************************/

#line 21 "src/Charmonizer/Core/OperatingSystem.h"
/* Charmonizer/Core/OperatingSystem.h - abstract an operating system down to a few
 * variables.
 */

#ifndef H_CHAZ_OPER_SYS
#define H_CHAZ_OPER_SYS

#define CHAZ_OS_POSIX    1
#define CHAZ_OS_CMD_EXE  2

/* Safely remove a file named [name]. Needed because of Windows quirks.
 * Returns true on success, false on failure.
 */
int
chaz_OS_remove(const char *name);

/* Invoke a command and attempt to suppress output from both stdout and stderr
 * (as if they had been sent to /dev/null).  If it's not possible to run the
 * command quietly, run it anyway.
 */
int
chaz_OS_run_quietly(const char *command);

/* Capture both stdout and stderr for a command to the supplied filepath.
 */
int
chaz_OS_run_redirected(const char *command, const char *path);

/* Run a command beginning with the name of an executable in the current
 * working directory and capture both stdout and stderr to the supplied
 * filepath.
 */
int
chaz_OS_run_local_redirected(const char *command, const char *path);

/* Run a command and return the output from stdout.
 */
char*
chaz_OS_run_and_capture(const char *command, size_t *output_len);

/* Attempt to create a directory.
 */
void
chaz_OS_mkdir(const char *filepath);

/* Attempt to remove a directory, which must be empty.
 */
void
chaz_OS_rmdir(const char *filepath);

/* Return the equivalent of /dev/null on this system.
 */
const char*
chaz_OS_dev_null(void);

/* Return the directory separator on this system.
 */
const char*
chaz_OS_dir_sep(void);

/* Return the shell type of this system.
 */
int
chaz_OS_shell_type(void);

/* Initialize the Charmonizer/Core/OperatingSystem module.
 */
void
chaz_OS_init(void);

#endif /* H_CHAZ_COMPILER */



/***************************************************************************/

#line 21 "src/Charmonizer/Core/Util.h"
/* Chaz/Core/Util.h -- miscellaneous utilities.
 */

#ifndef H_CHAZ_UTIL
#define H_CHAZ_UTIL 1

#include <stdio.h>
#include <stddef.h>
#include <stdarg.h>

extern int chaz_Util_verbosity;

/* Open a file (truncating if necessary) and write [content] to it.  Util_die() if
 * an error occurs.
 */
void
chaz_Util_write_file(const char *filename, const char *content);

/* Read an entire file into memory.
 */
char*
chaz_Util_slurp_file(const char *file_path, size_t *len_ptr);

/* Return a newly allocated copy of a NULL-terminated string.
 */
char*
chaz_Util_strdup(const char *string);

/* Join a NULL-terminated list of strings using a separator.
 */
char*
chaz_Util_join(const char *sep, ...);

/* Join a NULL-terminated list of strings using a separator.
 */
char*
chaz_Util_vjoin(const char *sep, va_list args);

/* Get the length of a file (may overshoot on text files under DOS).
 */
long
chaz_Util_flength(void *file);

/* Print an error message to stderr and exit.
 */
void
chaz_Util_die(const char *format, ...);

/* Print an error message to stderr.
 */
void
chaz_Util_warn(const char *format, ...);

/* Attept to delete a file.  Return true if the file is gone, whether or not
 * it was there to begin with.  Issue a warning and return false if the file
 * still exists.
 */
int
chaz_Util_remove_and_verify(const char *file_path);

/* Attempt to open a file for reading, then close it immediately.
 */
int
chaz_Util_can_open_file(const char *file_path);

#endif /* H_CHAZ_UTIL */



/***************************************************************************/

#line 21 "src/Charmonizer/Probe.h"
#ifndef H_CHAZ
#define H_CHAZ 1

#include <stddef.h>
#include <stdio.h>

struct chaz_CLI;

/* Parse command line arguments, initializing and filling in the supplied
 * `args` struct.
 *
 *     APP_NAME --cc=CC_COMMAND
 *              [--enable-c]
 *              [--enable-perl]
 *              [--enable-python]
 *              [--enable-ruby]
 *              [-- [CFLAGS]]
 *
 * @return true if argument parsing proceeds without incident, false if
 * unexpected arguments are encountered or values are missing or invalid.
 */
int
chaz_Probe_parse_cli_args(int argc, const char *argv[],
                          struct chaz_CLI *cli);

/* Exit after printing usage instructions to stderr.
 */
void
chaz_Probe_die_usage(void);

/* Set up the Charmonizer environment.
 *
 * If the environment variable CHARM_VERBOSITY has been set, it will be
 * processed at this time:
 *
 *      0 - silent
 *      1 - normal
 *      2 - debugging
 */
void
chaz_Probe_init(struct chaz_CLI *cli);

/* Clean up the Charmonizer environment -- deleting tempfiles, etc.  This
 * should be called only after everything else finishes.
 */
void
chaz_Probe_clean_up(void);

/* Return an integer version of the GCC version number which is
 * (10000 * __GNU_C__ + 100 * __GNUC_MINOR__ + __GNUC_PATCHLEVEL__).
 */
int
chaz_Probe_gcc_version_num(void);

/* If the compiler is GCC (or claims compatibility), return an X.Y.Z string
 * version of the GCC version; otherwise, return NULL.
 */
const char*
chaz_Probe_gcc_version(void);

/* Return the integer version of MSVC defined by _MSC_VER
 */
int
chaz_Probe_msvc_version_num(void);

#endif /* Include guard. */



/***************************************************************************/

#line 21 "src/Charmonizer/Probe/AtomicOps.h"
/* Charmonizer/Probe/AtomicOps.h
 */

#ifndef H_CHAZ_ATOMICOPS
#define H_CHAZ_ATOMICOPS

#include <stdio.h>

/* Run the AtomicOps module.
 *
 * These following symbols will be defined if the associated headers are
 * available:
 *
 * HAS_LIBKERN_OSATOMIC_H  <libkern/OSAtomic.h> (Mac OS X)
 * HAS_SYS_ATOMIC_H        <sys/atomic.h>       (Solaris)
 * HAS_INTRIN_H            <intrin.h>           (Windows)
 *
 * This symbol is defined if OSAtomicCompareAndSwapPtr is available:
 *
 * HAS_OSATOMIC_CAS_PTR
 */
void chaz_AtomicOps_run(void);

#endif /* H_CHAZ_ATOMICOPS */




/***************************************************************************/

#line 21 "src/Charmonizer/Probe/Booleans.h"
/* Charmonizer/Probe/Booleans.h -- bool type.
 *
 * If stdbool.h is is available, it will be pound-included in the configuration
 * header.  If it is not, the following typedef will be defined:
 *
 * bool
 *
 * These symbols will be defined if they are not already:
 *
 * true
 * false
 */

#ifndef H_CHAZ_BOOLEANS
#define H_CHAZ_BOOLEANS

#include <stdio.h>

/* Run the Booleans module.
 */
void chaz_Booleans_run(void);

#endif /* H_CHAZ_BOOLEANS */




/***************************************************************************/

#line 21 "src/Charmonizer/Probe/BuildEnv.h"
/* Charmonizer/Probe/BuildEnv.h -- Build environment.
 *
 * Capture various information about the build environment, including the C
 * compiler's interface, the shell, the operating system, etc.
 *
 * The following symbols will be defined:
 *
 * CC - String representation of the C compiler executable.
 * CFLAGS - C compiler flags.
 * EXTRA_CFLAGS - Extra C compiler flags.
 */

#ifndef H_CHAZ_BUILDENV
#define H_CHAZ_BUILDENV

#include <stdio.h>

/* Run the BuildEnv module.
 */
void chaz_BuildEnv_run(void);

#endif /* H_CHAZ_BUILDENV */




/***************************************************************************/

#line 21 "src/Charmonizer/Probe/DirManip.h"
/* Charmonizer/Probe/DirManip.h
 */

#ifndef H_CHAZ_DIRMANIP
#define H_CHAZ_DIRMANIP

/* The DirManip module exports or aliases symbols related to directory and file
 * manipulation.
 *
 * Defined if the header files dirent.h and direct.h are available, respectively:
 *
 * HAS_DIRENT_H
 * HAS_DIRECT_H
 *
 * Defined if struct dirent has these members.
 *
 * HAS_DIRENT_D_NAMLEN
 * HAS_DIRENT_D_TYPE
 *
 * The "makedir" macro will be aliased to the POSIX-specified two-argument
 * "mkdir" interface:
 *
 * makedir
 *
 * On some systems, the second argument to makedir will be ignored, in which
 * case this symbol will be true; otherwise, it will be false: (TODO: This
 * isn't verified and may sometimes be incorrect.)
 *
 * MAKEDIR_MODE_IGNORED
 *
 * String representing the system's directory separator:
 *
 * DIR_SEP
 *
 * True if the remove() function removes directories, false otherwise:
 *
 * REMOVE_ZAPS_DIRS
 */
void chaz_DirManip_run(void);

#endif /* H_CHAZ_DIR_SEP */




/***************************************************************************/

#line 21 "src/Charmonizer/Probe/Floats.h"
/* Charmonizer/Probe/Floats.h -- floating point types.
 *
 * The following symbols will be created if the platform supports IEEE 754
 * floating point types:
 *
 * F32_NAN
 * F32_INF
 * F32_NEGINF
 * F64_NAN
 * F64_INF
 * F64_NEGINF
 *
 * TODO: Actually test to see whether IEEE 754 is supported, rather than just
 * lying about it.
 */

#ifndef H_CHAZ_FLOATS
#define H_CHAZ_FLOATS

/* Run the Floats module.
 */
void
chaz_Floats_run(void);

/* Return the name of the math library to link against or NULL.
 */
const char*
chaz_Floats_math_library(void);

#endif /* H_CHAZ_FLOATS */




/***************************************************************************/

#line 21 "src/Charmonizer/Probe/FuncMacro.h"
/* Charmonizer/Probe/FuncMacro.h
 */

#ifndef H_CHAZ_FUNC_MACRO
#define H_CHAZ_FUNC_MACRO

#include <stdio.h>

/* Run the FuncMacro module.
 *
 * If __func__ successfully resolves, this will be defined:
 *
 * HAS_ISO_FUNC_MACRO
 *
 * If __FUNCTION__ successfully resolves, this will be defined:
 *
 * HAS_GNUC_FUNC_MACRO
 *
 * If one or the other succeeds, these will be defined:
 *
 * HAS_FUNC_MACRO
 * FUNC_MACRO
 *
 * The "inline" keyword will also be probed for.  If it is available, the
 * following macro will be defined to "inline", otherwise it will be defined
 * to nothing.
 *
 * INLINE
 */
void chaz_FuncMacro_run(void);

#endif /* H_CHAZ_FUNC_MACRO */




/***************************************************************************/

#line 20 "src/Charmonizer/Probe/Headers.h"
/* Charmonizer/Probe/Headers.h
 */

#ifndef H_CHAZ_HEADERS
#define H_CHAZ_HEADERS

#include <stdio.h>
/* #include "Charmonizer/Core/Defines.h" */

/* Check whether a particular header file is available.  The test-compile is
 * only run the first time a given request is made.
 */
int
chaz_Headers_check(const char *header_name);

/* Run the Headers module.
 *
 * Exported symbols:
 *
 * If HAS_C89 is declared, this system has all the header files described in
 * Ansi C 1989.  HAS_C90 is a synonym.  (It would be surprising if they are
 * not defined, because Charmonizer itself assumes C89.)
 *
 * HAS_C89
 * HAS_C90
 *
 * One symbol is exported for each C89 header file:
 *
 * HAS_ASSERT_H
 * HAS_CTYPE_H
 * HAS_ERRNO_H
 * HAS_FLOAT_H
 * HAS_LIMITS_H
 * HAS_LOCALE_H
 * HAS_MATH_H
 * HAS_SETJMP_H
 * HAS_SIGNAL_H
 * HAS_STDARG_H
 * HAS_STDDEF_H
 * HAS_STDIO_H
 * HAS_STDLIB_H
 * HAS_STRING_H
 * HAS_TIME_H
 *
 * One symbol is exported for every POSIX header present, and HAS_POSIX is
 * exported if they're all there.
 *
 * HAS_POSIX
 *
 * HAS_CPIO_H
 * HAS_DIRENT_H
 * HAS_FCNTL_H
 * HAS_GRP_H
 * HAS_PWD_H
 * HAS_SYS_STAT_H
 * HAS_SYS_TIMES_H
 * HAS_SYS_TYPES_H
 * HAS_SYS_UTSNAME_H
 * HAS_WAIT_H
 * HAS_TAR_H
 * HAS_TERMIOS_H
 * HAS_UNISTD_H
 * HAS_UTIME_H
 *
 * If pthread.h is available, this will be exported:
 *
 * HAS_PTHREAD_H
 */
void
chaz_Headers_run(void);

#endif /* H_CHAZ_HEADERS */




/***************************************************************************/

#line 21 "src/Charmonizer/Probe/Integers.h"
/* Charmonizer/Probe/Integers.h -- info about integer types and sizes.
 *
 * One or the other of these will be defined, depending on whether the
 * processor is big-endian or little-endian.
 *
 * BIG_END
 * LITTLE_END
 *
 * These will always be defined:
 *
 * SIZEOF_CHAR
 * SIZEOF_SHORT
 * SIZEOF_INT
 * SIZEOF_LONG
 * SIZEOF_PTR
 *
 * If long longs are available these symbols will be defined:
 *
 * HAS_LONG_LONG
 * SIZEOF_LONG_LONG
 *
 * Similarly, with the __int64 type (the sizeof is included for completeness):
 *
 * HAS___INT64
 * SIZEOF___INT64
 *
 * If the inttypes.h or stdint.h header files are available, these may be
 * defined:
 *
 * HAS_INTTYPES_H
 * HAS_STDINT_H
 *
 * If stdint.h is is available, it will be pound-included in the configuration
 * header.  If it is not, the following typedefs and macros will be defined if
 * possible:
 *
 * int8_t
 * int16_t
 * int32_t
 * int64_t
 * uint8_t
 * uint16_t
 * uint32_t
 * uint64_t
 * INT8_MAX
 * INT16_MAX
 * INT32_MAX
 * INT64_MAX
 * INT8_MIN
 * INT16_MIN
 * INT32_MIN
 * INT64_MIN
 * UINT8_MAX
 * UINT16_MAX
 * UINT32_MAX
 * UINT64_MAX
 * SIZE_MAX
 * INT32_C
 * INT64_C
 * UINT32_C
 * UINT64_C
 *
 * If inttypes.h is is available, it will be pound-included in the
 * configuration header.  If it is not, the following macros will be defined if
 * possible:
 *
 * PRId64
 * PRIu64
 *
 * Availability of integer types is indicated by which of these are defined:
 *
 * HAS_INT8_T
 * HAS_INT16_T
 * HAS_INT32_T
 * HAS_INT64_T
 *
 * If 64-bit integers are available, this macro will promote pointers to i64_t
 * safely.
 *
 * PTR_TO_I64(ptr)
 */

#ifndef H_CHAZ_INTEGERS
#define H_CHAZ_INTEGERS

#include <stdio.h>

/* Run the Integers module.
 */
void chaz_Integers_run(void);

#endif /* H_CHAZ_INTEGERS */




/***************************************************************************/

#line 21 "src/Charmonizer/Probe/LargeFiles.h"
/* Charmonizer/Probe/LargeFiles.h
 */

#ifndef H_CHAZ_LARGE_FILES
#define H_CHAZ_LARGE_FILES

#include <stdio.h>

/* The LargeFiles module attempts to detect these symbols or alias them to
 * synonyms:
 *
 * off64_t
 * fopen64
 * ftello64
 * fseeko64
 * lseek64
 * pread64
 *
 * If off64_t or its equivalent is available, this will be defined:
 *
 * HAS_64BIT_OFFSET_TYPE
 *
 * If 64-bit variants of fopen, ftell, and fseek are available, this will be
 * defined:
 *
 * HAS_64BIT_STDIO
 *
 * If 64-bit variants of pread and lseek are available, then corresponding
 * symbols will be defined:
 *
 * HAS_64BIT_PREAD
 * HAS_64BIT_LSEEK
 *
 * Use of the off64_t symbol may require sys/types.h.
 */
void chaz_LargeFiles_run(void);

#endif /* H_CHAZ_LARGE_FILES */



/***************************************************************************/

#line 21 "src/Charmonizer/Probe/Memory.h"
/* Charmonizer/Probe/Memory.h
 */

#ifndef H_CHAZ_MEMORY
#define H_CHAZ_MEMORY

/* The Memory module attempts to detect these symbols or alias them to
 * synonyms:
 *
 * alloca
 *
 * These following symbols will be defined if the associated headers are
 * available:
 *
 * HAS_SYS_MMAN_H          <sys/mman.h>
 * HAS_ALLOCA_H            <alloca.h>
 * HAS_MALLOC_H            <malloc.h>
 *
 * Defined if alloca() is available via stdlib.h:
 *
 * ALLOCA_IN_STDLIB_H
 */
void chaz_Memory_run(void);

#endif /* H_CHAZ_MEMORY */




/***************************************************************************/

#line 21 "src/Charmonizer/Probe/RegularExpressions.h"
/* Charmonizer/Probe/RegularExpressions.h -- regular expressions.
 */

#ifndef H_CHAZ_REGULAREXPRESSIONS
#define H_CHAZ_REGULAREXPRESSIONS

/* Run the RegularExpressions module.
 */
void chaz_RegularExpressions_run(void);

#endif /* H_CHAZ_REGULAREXPRESSIONS */




/***************************************************************************/

#line 21 "src/Charmonizer/Probe/Strings.h"
/* Charmonizer/Probe/Strings.h
 */

#ifndef H_CHAZ_STRINGS
#define H_CHAZ_STRINGS

/* The Strings module attempts to detect whether snprintf works as specified
 * by the C99 standard. It also looks for system-specific functions which can
 * be used to emulate snprintf.
 */
void chaz_Strings_run(void);

#endif /* H_CHAZ_STRINGS */



/***************************************************************************/

#line 21 "src/Charmonizer/Probe/SymbolVisibility.h"
/* Charmonizer/Probe/SymbolVisibility.h
 */

#ifndef H_CHAZ_SYMBOLVISIBILITY
#define H_CHAZ_SYMBOLVISIBILITY

void chaz_SymbolVisibility_run(void);

#endif /* H_CHAZ_SYMBOLVISIBILITY */




/***************************************************************************/

#line 21 "src/Charmonizer/Probe/UnusedVars.h"
/* Charmonizer/Probe/UnusedVars.h
 */

#ifndef H_CHAZ_UNUSED_VARS
#define H_CHAZ_UNUSED_VARS

#include <stdio.h>

/* Run the UnusedVars module.
 *
 * These symbols are exported:
 *
 * UNUSED_VAR(var)
 * UNREACHABLE_RETURN(type)
 *
 */
void chaz_UnusedVars_run(void);

#endif /* H_CHAZ_UNUSED_VARS */




/***************************************************************************/

#line 21 "src/Charmonizer/Probe/VariadicMacros.h"
/* Charmonizer/Probe/VariadicMacros.h
 */

#ifndef H_CHAZ_VARIADIC_MACROS
#define H_CHAZ_VARIADIC_MACROS

#include <stdio.h>

/* Run the VariadicMacros module.
 *
 * If your compiler supports ISO-style variadic macros, this will be defined:
 *
 * HAS_ISO_VARIADIC_MACROS
 *
 * If your compiler supports GNU-style variadic macros, this will be defined:
 *
 * HAS_GNUC_VARIADIC_MACROS
 *
 * If you have at least one of the above, this will be defined:
 *
 * HAS_VARIADIC_MACROS
 */
void chaz_VariadicMacros_run(void);

#endif /* H_CHAZ_VARIADIC_MACROS */




/***************************************************************************/

#line 17 "src/Charmonizer/Core/CFlags.c"
#include <string.h>
#include <stdlib.h>
/* #include "Charmonizer/Core/CFlags.h" */
/* #include "Charmonizer/Core/Compiler.h" */
/* #include "Charmonizer/Core/Util.h" */
/* #include "Charmonizer/Core/OperatingSystem.h" */

struct chaz_CFlags {
    int   style;
    char *string;
};

chaz_CFlags*
chaz_CFlags_new(int style) {
    chaz_CFlags *flags = (chaz_CFlags*)malloc(sizeof(chaz_CFlags));
    flags->style  = style;
    flags->string = chaz_Util_strdup("");
    return flags;
}

void
chaz_CFlags_destroy(chaz_CFlags *flags) {
    free(flags->string);
    free(flags);
}

const char*
chaz_CFlags_get_string(chaz_CFlags *flags) {
    return flags->string;
}

void
chaz_CFlags_append(chaz_CFlags *flags, const char *string) {
    char *new_string;
    if (flags->string[0] == '\0') {
        new_string = chaz_Util_strdup(string);
    }
    else {
        new_string = chaz_Util_join(" ", flags->string, string, NULL);
    }
    free(flags->string);
    flags->string = new_string;
}

void
chaz_CFlags_clear(chaz_CFlags *flags) {
    if (flags->string[0] != '\0') {
        free(flags->string);
        flags->string = chaz_Util_strdup("");
    }
}

void
chaz_CFlags_set_output_obj(chaz_CFlags *flags, const char *filename) {
    const char *output;
    char *string;
    if (flags->style == CHAZ_CFLAGS_STYLE_MSVC) {
        output = "/c /Fo";
    }
    else {
        /* POSIX */
        output = "-c -o ";
    }
    string = chaz_Util_join("", output, filename, NULL);
    chaz_CFlags_append(flags, string);
    free(string);
}

void
chaz_CFlags_set_output_exe(chaz_CFlags *flags, const char *filename) {
    const char *output;
    char *string;
    if (flags->style == CHAZ_CFLAGS_STYLE_MSVC) {
        output = "/Fe";
    }
    else {
        /* POSIX */
        output = "-o ";
    }
    string = chaz_Util_join("", output, filename, NULL);
    chaz_CFlags_append(flags, string);
    free(string);
}

void
chaz_CFlags_add_define(chaz_CFlags *flags, const char *name,
                       const char *value) {
    const char *define;
    char *string;
    if (flags->style == CHAZ_CFLAGS_STYLE_MSVC) {
        define = "/D";
    }
    else {
        /* POSIX */
        define = "-D ";
    }
    if (value) {
        string = chaz_Util_join("", define, name, "=", value, NULL);
    }
    else {
        string = chaz_Util_join("", define, name, NULL);
    }
    chaz_CFlags_append(flags, string);
    free(string);
}

void
chaz_CFlags_add_include_dir(chaz_CFlags *flags, const char *dir) {
    const char *include;
    char *string;
    if (flags->style == CHAZ_CFLAGS_STYLE_MSVC)  {
        include = "/I ";
    }
    else {
        /* POSIX */
        include = "-I ";
    }
    string = chaz_Util_join("", include, dir, NULL);
    chaz_CFlags_append(flags, string);
    free(string);
}

void
chaz_CFlags_enable_optimization(chaz_CFlags *flags) {
    const char *string;
    if (flags->style == CHAZ_CFLAGS_STYLE_MSVC) {
        string = "/O2";
    }
    else if (flags->style == CHAZ_CFLAGS_STYLE_GNU) {
        string = "-O2";
    }
    else if (flags->style == CHAZ_CFLAGS_STYLE_SUN_C) {
        string = "-xO4";
    }
    else {
        /* POSIX */
        string = "-O 1";
    }
    chaz_CFlags_append(flags, string);
}

void
chaz_CFlags_enable_debugging(chaz_CFlags *flags) {
    if (flags->style == CHAZ_CFLAGS_STYLE_GNU
        || flags->style == CHAZ_CFLAGS_STYLE_SUN_C) {
        chaz_CFlags_append(flags, "-g");
    }
}

void
chaz_CFlags_disable_strict_aliasing(chaz_CFlags *flags) {
    if (flags->style == CHAZ_CFLAGS_STYLE_MSVC) {
        return;
    }
    else if (flags->style == CHAZ_CFLAGS_STYLE_GNU) {
        chaz_CFlags_append(flags, "-fno-strict-aliasing");
    }
    else if (flags->style == CHAZ_CFLAGS_STYLE_SUN_C) {
        chaz_CFlags_append(flags, "-xalias_level=any");
    }
    else {
        chaz_Util_die("Don't know how to disable strict aliasing with '%s'",
                      chaz_CC_get_cc());
    }
}

void
chaz_CFlags_set_warnings_as_errors(chaz_CFlags *flags) {
    const char *string;
    if (flags->style == CHAZ_CFLAGS_STYLE_MSVC) {
        string = "/WX";
    }
    else if (flags->style == CHAZ_CFLAGS_STYLE_GNU) {
        string = "-Werror";
    }
    else if (flags->style == CHAZ_CFLAGS_STYLE_SUN_C) {
        string = "-errwarn=%all";
    }
    else {
        chaz_Util_die("Don't know how to set warnings as errors with '%s'",
                      chaz_CC_get_cc());
    }
    chaz_CFlags_append(flags, string);
}

void
chaz_CFlags_compile_shared_library(chaz_CFlags *flags) {
    const char *string;
    if (flags->style == CHAZ_CFLAGS_STYLE_MSVC) {
        string = "/MD";
    }
    else if (flags->style == CHAZ_CFLAGS_STYLE_GNU) {
        int binary_format = chaz_CC_binary_format();
        if (binary_format == CHAZ_CC_BINFMT_MACHO) {
            string = "-fno-common";
        }
        else if (binary_format == CHAZ_CC_BINFMT_ELF) {
            string = "-fPIC";
        }
        else {
            /* MinGW. */
            return;
        }
    }
    else if (flags->style == CHAZ_CFLAGS_STYLE_SUN_C) {
        string = "-KPIC";
    }
    else {
        return;
    }
    chaz_CFlags_append(flags, string);
}

void
chaz_CFlags_hide_symbols(chaz_CFlags *flags) {
    if (flags->style == CHAZ_CFLAGS_STYLE_GNU) {
        if (chaz_CC_binary_format() != CHAZ_CC_BINFMT_PE) {
            chaz_CFlags_append(flags, "-fvisibility=hidden");
        }
    }
    else if (flags->style == CHAZ_CFLAGS_STYLE_SUN_C) {
        if (chaz_CC_sun_c_version_num() >= 0x550) {
            /* Sun Studio 8. */
            chaz_CFlags_append(flags, "-xldscope=hidden");
        }
    }
}

void
chaz_CFlags_link_shared_library(chaz_CFlags *flags, const char *basename,
                                const char *version,
                                const char *major_version) {
    char *string = NULL;

    if (flags->style == CHAZ_CFLAGS_STYLE_MSVC) {
        string = chaz_Util_strdup("/DLL");
    }
    else if (flags->style == CHAZ_CFLAGS_STYLE_GNU) {
        int binary_format = chaz_CC_binary_format();

        if (binary_format == CHAZ_CC_BINFMT_MACHO) {
            string = chaz_Util_join(" ", "-dynamiclib", "-current_version",
                                    version, "-compatibility_version",
                                    major_version, NULL);
        }
        else if (binary_format == CHAZ_CC_BINFMT_ELF) {
            string = chaz_Util_join("", "-shared -Wl,-soname,lib", basename,
                                    ".so.", major_version, NULL);
        }
        else if (binary_format == CHAZ_CC_BINFMT_PE) {
            string = chaz_Util_join("", "-shared -Wl,--out-implib,lib",
                                    basename, "-", major_version, ".dll.a",
                                    NULL);
        }
    }
    else if (flags->style == CHAZ_CFLAGS_STYLE_SUN_C) {
        string = chaz_Util_join("", "-G -h lib", basename, ".so.",
                                major_version, NULL);
    }
    else {
        chaz_Util_die("Don't know how to link a shared library with '%s'",
                      chaz_CC_get_cc());
    }

    if (string) {
        chaz_CFlags_append(flags, string);
        free(string);
    }
}

void
chaz_CFlags_set_link_output(chaz_CFlags *flags, const char *filename) {
    const char *output;
    char *string;
    if (flags->style == CHAZ_CFLAGS_STYLE_MSVC) {
        output = "/OUT:";
    }
    else {
        output = "-o ";
    }
    string = chaz_Util_join("", output, filename, NULL);
    chaz_CFlags_append(flags, string);
    free(string);
}

void
chaz_CFlags_add_library_path(chaz_CFlags *flags, const char *directory) {
    const char *lib_path;
    char *string;
    if (flags->style == CHAZ_CFLAGS_STYLE_MSVC) {
        if (strcmp(directory, ".") == 0) {
            /* The MS linker searches the current directory by default. */
            return;
        }
        else {
            lib_path = "/LIBPATH:";
        }
    }
    else {
        lib_path = "-L ";
    }
    string = chaz_Util_join("", lib_path, directory, NULL);
    chaz_CFlags_append(flags, string);
    free(string);
}

void
chaz_CFlags_add_shared_lib(chaz_CFlags *flags, const char *dir,
                           const char *basename, const char *major_version) {
    int binfmt = chaz_CC_binary_format();
    char *filename;
    if (binfmt == CHAZ_CC_BINFMT_PE) {
        filename = chaz_CC_import_lib_filename(dir, basename, major_version);
    }
    else {
        filename = chaz_CC_shared_lib_filename(dir, basename, major_version);
    }
    chaz_CFlags_append(flags, filename);
    free(filename);
}

void
chaz_CFlags_add_external_lib(chaz_CFlags *flags, const char *library) {
    char *string;
    if (flags->style == CHAZ_CFLAGS_STYLE_MSVC) {
        string = chaz_Util_join("", library, ".lib", NULL);
    }
    else {
        string = chaz_Util_join(" ", "-l", library, NULL);
    }
    chaz_CFlags_append(flags, string);
    free(string);
}

void
chaz_CFlags_add_rpath(chaz_CFlags *flags, const char *path) {
    char *string;

    if (chaz_CC_binary_format() != CHAZ_CC_BINFMT_ELF) { return; }

    if (flags->style == CHAZ_CFLAGS_STYLE_GNU) {
        /* If "new dtags" are enabled by default, DT_RUNPATH is set instead of
         * DT_RPATH. Unfortunately, DT_RUNPATH is not applied transitively
         * when searching for indirect dependencies. See
         *
         * https://bugs.launchpad.net/ubuntu/+source/eglibc/+bug/1253638
         * https://sourceware.org/bugzilla/show_bug.cgi?id=13945
         */
        string = chaz_Util_join("", "-Wl,--disable-new-dtags -Wl,-rpath,",
                                path, NULL);
    }
    else if (flags->style == CHAZ_CFLAGS_STYLE_SUN_C) {
        string = chaz_Util_join(" ", "-R", path, NULL);
    }
    else {
        chaz_Util_die("Don't know how to set rpath with '%s'",
                      chaz_CC_get_cc());
    }

    chaz_CFlags_append(flags, string);
    free(string);
}

void
chaz_CFlags_enable_code_coverage(chaz_CFlags *flags) {
    if (flags->style == CHAZ_CFLAGS_STYLE_GNU) {
        chaz_CFlags_append(flags, "--coverage");
    }
    else {
        chaz_Util_die("Don't know how to enable code coverage with '%s'",
                      chaz_CC_get_cc());
    }
}



/***************************************************************************/

#line 17 "src/Charmonizer/Core/CLI.c"
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
/* #include "Charmonizer/Core/CLI.h" */
/* #include "Charmonizer/Core/Util.h" */

typedef struct chaz_CLIOption {
    char *name;
    char *help;
    char *value;
    int   defined;
    int   flags;
} chaz_CLIOption;

struct chaz_CLI {
    char *name;
    char *desc;
    char *usage;
    char *help;
    chaz_CLIOption *opts;
    int   num_opts;
};

static void
S_chaz_CLI_error(chaz_CLI *self, const char *pattern, ...) {
    va_list ap;
    (void)self;
    if (chaz_Util_verbosity > 0) {
        va_start(ap, pattern);
        vfprintf(stderr, pattern, ap);
        va_end(ap);
        fprintf(stderr, "\n");
    }
}

static void
S_chaz_CLI_rebuild_help(chaz_CLI *self) {
    int i;
    size_t amount = 200; /* Length of section headers. */

    /* Allocate space. */
    if (self->usage) {
        amount += strlen(self->usage);
    }
    else {
        amount += strlen(self->name);
    }
    if (self->desc) {
        amount += strlen(self->desc);
    }
    for (i = 0; i < self->num_opts; i++) {
        chaz_CLIOption *opt = &self->opts[i];
        amount += 24 + 2 * strlen(opt->name);
        if (opt->flags) {
            amount += strlen(opt->name);
        }
        if (opt->help) {
            amount += strlen(opt->help);
        }
    }
    free(self->help);
    self->help = (char*)malloc(amount);
    self->help[0] = '\0';

    /* Accumulate "help" string. */
    if (self->usage) {
        strcat(self->help, self->usage);
    }
    else {
        strcat(self->help, "Usage: ");
        strcat(self->help, self->name);
        if (self->num_opts) {
            strcat(self->help, " [OPTIONS]");
        }
    }
    if (self->desc) {
        strcat(self->help, "\n\n");
        strcat(self->help, self->desc);
    }
    strcat(self->help, "\n");
    if (self->num_opts) {
        strcat(self->help, "\nArguments:\n");
        for (i = 0; i < self->num_opts; i++) {
            chaz_CLIOption *opt = &self->opts[i];
            size_t line_start = strlen(self->help);
            size_t current_len;

            strcat(self->help, "  --");
            strcat(self->help, opt->name);
            current_len = strlen(self->help);
            if (opt->flags) {
                int j;
                if (opt->flags & CHAZ_CLI_ARG_OPTIONAL) {
                    self->help[current_len++] = '[';
                }
                self->help[current_len++] = '=';
                for (j = 0; opt->name[j]; j++) {
                    unsigned char c = (unsigned char)opt->name[j];
                    self->help[current_len++] = toupper(c);
                }
                if (opt->flags & CHAZ_CLI_ARG_OPTIONAL) {
                    self->help[current_len++] = ']';
                }
                self->help[current_len] = '\0';
            }
            if (opt->help) {
                self->help[current_len++] = ' ';
                while (current_len - line_start < 25) {
                    self->help[current_len++] = ' ';
                }
                self->help[current_len] = '\0';
                strcpy(self->help + current_len, opt->help);
            }
            strcat(self->help, "\n");
        }
    }
    strcat(self->help, "\n");
}

static chaz_CLIOption*
S_chaz_CLI_find_opt(chaz_CLI *self, const char *name) {
    int i;
    for (i = 0; i < self->num_opts; i++) {
        chaz_CLIOption *opt = &self->opts[i];
        if (strcmp(opt->name, name) == 0) {
            return opt;
        }
    }
    return NULL;
}

chaz_CLI*
chaz_CLI_new(const char *name, const char *description) {
    chaz_CLI *self  = calloc(1, sizeof(chaz_CLI));
    self->name      = chaz_Util_strdup(name ? name : "PROGRAM");
    self->desc      = description ? chaz_Util_strdup(description) : NULL;
    self->help      = NULL;
    self->opts      = NULL;
    self->num_opts  = 0;
    S_chaz_CLI_rebuild_help(self);
    return self;
}

void
chaz_CLI_destroy(chaz_CLI *self) {
    int i;
    for (i = 0; i < self->num_opts; i++) {
        chaz_CLIOption *opt = &self->opts[i];
        free(opt->name);
        free(opt->help);
        free(opt->value);
    }
    free(self->name);
    free(self->desc);
    free(self->opts);
    free(self->usage);
    free(self->help);
    free(self);
}

void
chaz_CLI_set_usage(chaz_CLI *self, const char *usage) {
    free(self->usage);
    self->usage = chaz_Util_strdup(usage);
}

const char*
chaz_CLI_help(chaz_CLI *self) {
    return self->help;
}

int
chaz_CLI_register(chaz_CLI *self, const char *name, const char *help,
                  int flags) {
    int rank;
    int i;
    int arg_required = !!(flags & CHAZ_CLI_ARG_REQUIRED);
    int arg_optional = !!(flags & CHAZ_CLI_ARG_OPTIONAL);

    /* Validate flags */
    if (arg_required && arg_optional) {
        S_chaz_CLI_error(self, "Conflicting flags: value both optional "
                         "and required");
        return 0;
    }

    /* Insert new option.  Keep options sorted by name. */
    for (rank = self->num_opts; rank > 0; rank--) {
        int comparison = strcmp(name, self->opts[rank - 1].name);
        if (comparison == 0) {
            S_chaz_CLI_error(self, "Option '%s' already registered", name);
            return 0;
        }
        else if (comparison > 0) {
            break;
        }
    }
    self->num_opts += 1;
    self->opts = realloc(self->opts, self->num_opts * sizeof(chaz_CLIOption));
    for (i = self->num_opts - 1; i > rank; i--) {
        self->opts[i] = self->opts[i - 1];
    }
    self->opts[rank].name    = chaz_Util_strdup(name);
    self->opts[rank].help    = help ? chaz_Util_strdup(help) : NULL;
    self->opts[rank].flags   = flags;
    self->opts[rank].defined = 0;
    self->opts[rank].value   = NULL;

    /* Update `help` with new option. */
    S_chaz_CLI_rebuild_help(self);

    return 1;
}

int
chaz_CLI_set(chaz_CLI *self, const char *name, const char *value) {
    chaz_CLIOption *opt = S_chaz_CLI_find_opt(self, name);
    if (opt == NULL) {
        S_chaz_CLI_error(self, "Attempt to set unknown option: '%s'", name);
        return 0;
    }
    if (opt->defined) {
        S_chaz_CLI_error(self, "'%s' specified multiple times", name);
        return 0;
    }
    opt->defined = 1;
    if (opt->flags == CHAZ_CLI_NO_ARG) {
        if (value != NULL) {
            S_chaz_CLI_error(self, "'%s' expects no value", name);
            return 0;
        }
    }
    else {
        if (value == NULL) {
            S_chaz_CLI_error(self, "'%s' expects a value", name);
            return 0;
        }
        opt->value = chaz_Util_strdup(value);
    }
    return 1;
}

int
chaz_CLI_unset(chaz_CLI *self, const char *name) {
    chaz_CLIOption *opt = S_chaz_CLI_find_opt(self, name);
    if (opt == NULL) {
        S_chaz_CLI_error(self, "Attempt to unset unknown option: '%s'", name);
        return 0;
    }
    free(opt->value);
    opt->value = NULL;
    opt->defined = 0;
    return 1;
}

int
chaz_CLI_defined(chaz_CLI *self, const char *name) {
    chaz_CLIOption *opt = S_chaz_CLI_find_opt(self, name);
    if (opt == NULL) {
        S_chaz_CLI_error(self, "Inquiry for unknown option: '%s'", name);
        return 0;
    }
    return opt->defined;
}

long
chaz_CLI_longval(chaz_CLI *self, const char *name) {
    chaz_CLIOption *opt = S_chaz_CLI_find_opt(self, name);
    if (opt == NULL) {
        S_chaz_CLI_error(self, "Longval request for unknown option: '%s'",
                         name);
        return 0;
    }
    if (!opt->defined || !opt->value) {
        return 0;
    }
    return strtol(opt->value, NULL, 10);
}

const char*
chaz_CLI_strval(chaz_CLI *self, const char *name) {
    chaz_CLIOption *opt = S_chaz_CLI_find_opt(self, name);
    if (opt == NULL) {
        S_chaz_CLI_error(self, "Strval request for unknown option: '%s'",
                         name);
        return 0;
    }
    return opt->value;
}

int
chaz_CLI_parse(chaz_CLI *self, int argc, const char *argv[]) {
    int i;
    char *name = NULL;
    size_t name_cap = 0;

    /* Parse most args. */
    for (i = 1; i < argc; i++) {
        const char *arg = argv[i];
        size_t name_len = 0;
        const char *value = NULL;

        /* Stop processing if we see `-` or `--`. */
        if (strcmp(arg, "--") == 0 || strcmp(arg, "-") == 0) {
            break;
        }

        if (strncmp(arg, "--", 2) != 0) {
            S_chaz_CLI_error(self, "Unexpected argument: '%s'", arg);
            free(name);
            return 0;
        }

        /* Extract the name of the argument, look for a potential value. */
        while (1) {
            char c = arg[name_len + 2];
            if (isalnum((unsigned char)c) || c == '-' || c == '_') {
                name_len++;
            }
            else if (c == '\0') {
                break;
            }
            else if (c == '=') {
                /* The rest of the arg is the value. */
                value = arg + 2 + name_len + 1;
                break;
            }
            else {
                free(name);
                S_chaz_CLI_error(self, "Malformed argument: '%s'", arg);
                return 0;
            }
        }
        if (name_len + 1 > name_cap) {
            name_cap = name_len + 1;
            name = (char*)realloc(name, name_cap);
        }
        memcpy(name, arg + 2, name_len);
        name[name_len] = '\0';

        if (value == NULL && i + 1 < argc) {
            /* Support both '--opt=val' and '--opt val' styles. */
            chaz_CLIOption *opt = S_chaz_CLI_find_opt(self, name);
            if (opt == NULL) {
                S_chaz_CLI_error(self, "Attempt to set unknown option: '%s'",
                                 name);
                free(name);
                return 0;
            }
            if (opt->flags != CHAZ_CLI_NO_ARG) {
                i++;
                value = argv[i];
            }
        }

        /* Attempt to set the option. */
        if (!chaz_CLI_set(self, name, value)) {
            free(name);
            return 0;
        }
    }

    free(name);

    for (i = 0; i < self->num_opts; i++) {
        chaz_CLIOption *opt = &self->opts[i];
        if (!opt->defined && (opt->flags & CHAZ_CLI_ARG_REQUIRED)) {
            S_chaz_CLI_error(self, "Option '%s' is required", opt->name);
            return 0;
        }
    }

    return 1;
}


/***************************************************************************/

#line 17 "src/Charmonizer/Core/Compiler.c"
#include <errno.h>
#include <string.h>
#include <stdlib.h>
/* #include "Charmonizer/Core/Util.h" */
/* #include "Charmonizer/Core/Compiler.h" */
/* #include "Charmonizer/Core/ConfWriter.h" */
/* #include "Charmonizer/Core/OperatingSystem.h" */

/* Detect binary format.
 */
static void
chaz_CC_detect_binary_format(const char *filename);

/** Return the numeric value of a macro or 0 if it isn't defined.
 */
static int
chaz_CC_eval_macro(const char *macro);

/* Detect macros which may help to identify some compilers.
 */
static void
chaz_CC_detect_known_compilers(void);

/** Build a library filename from its components.
 */
static char*
chaz_CC_build_lib_filename(const char *dir, const char *prefix,
                           const char *basename, const char *version,
                           const char *ext);

/* Temporary files. */
#define CHAZ_CC_TRY_SOURCE_PATH  "_charmonizer_try.c"
#define CHAZ_CC_TRY_BASENAME     "_charmonizer_try"
#define CHAZ_CC_TARGET_PATH      "_charmonizer_target"

/* Static vars. */
static struct {
    char     *cc_command;
    char     *cflags;
    char     *try_exe_name;
    char      exe_ext[10];
    char      shared_lib_ext[10];
    char      static_lib_ext[10];
    char      import_lib_ext[10];
    char      obj_ext[10];
    char      gcc_version_str[30];
    int       binary_format;
    int       cflags_style;
    int       intval___GNUC__;
    int       intval___GNUC_MINOR__;
    int       intval___GNUC_PATCHLEVEL__;
    int       intval__MSC_VER;
    int       intval___clang__;
    int       intval___SUNPRO_C;
    int       is_cygwin;
    int       is_mingw;
    chaz_CFlags *extra_cflags;
    chaz_CFlags *temp_cflags;
} chaz_CC = {
    NULL, NULL, NULL,
    "", "", "", "", "", "",
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    NULL, NULL
};

void
chaz_CC_init(const char *compiler_command, const char *compiler_flags) {
    const char *code = "int main() { return 0; }\n";
    int compile_succeeded = 0;

    if (chaz_Util_verbosity) { printf("Creating compiler object...\n"); }

    /* Assign, init. */
    chaz_CC.cc_command   = chaz_Util_strdup(compiler_command);
    chaz_CC.cflags       = chaz_Util_strdup(compiler_flags);
    chaz_CC.extra_cflags = NULL;
    chaz_CC.temp_cflags  = NULL;

    /* Set names for the targets which we "try" to compile. */
    strcpy(chaz_CC.exe_ext, ".exe");
    chaz_CC.try_exe_name
        = chaz_Util_join("", CHAZ_CC_TRY_BASENAME, chaz_CC.exe_ext, NULL);

    /* If we can't compile or execute anything, game over. */
    if (chaz_Util_verbosity) {
        printf("Trying to compile and execute a small test file...\n");
    }

    /* Try MSVC argument style. */
    if (!compile_succeeded) {
        chaz_CC.cflags_style = CHAZ_CFLAGS_STYLE_MSVC;
        if (!chaz_Util_remove_and_verify(chaz_CC.try_exe_name)) {
            chaz_Util_die("Failed to delete file '%s'", chaz_CC.try_exe_name);
        }
        compile_succeeded = chaz_CC_compile_exe(CHAZ_CC_TRY_SOURCE_PATH,
                                                CHAZ_CC_TRY_BASENAME, code);
    }

    /* Try POSIX argument style. */
    if (!compile_succeeded) {
        chaz_CC.cflags_style = CHAZ_CFLAGS_STYLE_POSIX;
        if (!chaz_Util_remove_and_verify(chaz_CC.try_exe_name)) {
            chaz_Util_die("Failed to delete file '%s'", chaz_CC.try_exe_name);
        }
        compile_succeeded = chaz_CC_compile_exe(CHAZ_CC_TRY_SOURCE_PATH,
                                                CHAZ_CC_TRY_BASENAME, code);
    }

    if (!compile_succeeded) {
        chaz_Util_die("Failed to compile a small test file");
    }
    chaz_CC_detect_binary_format(chaz_CC.try_exe_name);
    chaz_Util_remove_and_verify(chaz_CC.try_exe_name);

    chaz_CC_detect_known_compilers();

    if (chaz_CC.intval___GNUC__) {
        chaz_CC.cflags_style = CHAZ_CFLAGS_STYLE_GNU;
    }
    else if (chaz_CC.intval__MSC_VER) {
        chaz_CC.cflags_style = CHAZ_CFLAGS_STYLE_MSVC;
    }
    else if (chaz_CC.intval___SUNPRO_C) {
        chaz_CC.cflags_style = CHAZ_CFLAGS_STYLE_SUN_C;
    }
    else {
        chaz_CC.cflags_style = CHAZ_CFLAGS_STYLE_POSIX;
    }
    chaz_CC.extra_cflags = chaz_CFlags_new(chaz_CC.cflags_style);
    chaz_CC.temp_cflags  = chaz_CFlags_new(chaz_CC.cflags_style);

    /* File extensions. */
    if (chaz_CC.binary_format == CHAZ_CC_BINFMT_ELF) {
        if (chaz_Util_verbosity) {
            printf("Detected binary format: ELF\n");
        }
        strcpy(chaz_CC.exe_ext, "");
        strcpy(chaz_CC.shared_lib_ext, ".so");
        strcpy(chaz_CC.static_lib_ext, ".a");
        strcpy(chaz_CC.obj_ext, ".o");
    }
    else if (chaz_CC.binary_format == CHAZ_CC_BINFMT_MACHO) {
        if (chaz_Util_verbosity) {
            printf("Detected binary format: Mach-O\n");
        }
        strcpy(chaz_CC.exe_ext, "");
        strcpy(chaz_CC.shared_lib_ext, ".dylib");
        strcpy(chaz_CC.static_lib_ext, ".a");
        strcpy(chaz_CC.obj_ext, ".o");
    }
    else if (chaz_CC.binary_format == CHAZ_CC_BINFMT_PE) {
        if (chaz_Util_verbosity) {
            printf("Detected binary format: Portable Executable\n");
        }
        strcpy(chaz_CC.exe_ext, ".exe");
        strcpy(chaz_CC.shared_lib_ext, ".dll");
        if (chaz_CC.intval___GNUC__) {
            strcpy(chaz_CC.static_lib_ext, ".a");
            strcpy(chaz_CC.import_lib_ext, ".dll.a");
            strcpy(chaz_CC.obj_ext, ".o");
        }
        else {
            strcpy(chaz_CC.static_lib_ext, ".lib");
            strcpy(chaz_CC.import_lib_ext, ".lib");
            strcpy(chaz_CC.obj_ext, ".obj");
        }

        if (chaz_CC_has_macro("__CYGWIN__")) {
            chaz_CC.is_cygwin = 1;
        }
        if (chaz_CC_has_macro("__MINGW32__")) {
            chaz_CC.is_mingw = 1;
        }
    }
    else {
        chaz_Util_die("Failed to detect binary format");
    }

    free(chaz_CC.try_exe_name);
    chaz_CC.try_exe_name
        = chaz_Util_join("", CHAZ_CC_TRY_BASENAME, chaz_CC.exe_ext, NULL);
}

static void
chaz_CC_detect_binary_format(const char *filename) {
    char *output;
    size_t output_len;
    int binary_format = 0;

    output = chaz_Util_slurp_file(filename, &output_len);

    /* ELF. */
    if (binary_format == 0 && output_len >= 4
        && memcmp(output, "\x7F" "ELF", 4) == 0
       ) {
        binary_format = CHAZ_CC_BINFMT_ELF;
    }

    /* Macho-O. */
    if (binary_format == 0 && output_len >= 4
        && (memcmp(output, "\xCA\xFE\xBA\xBE", 4) == 0      /* Fat binary. */
            || memcmp(output, "\xFE\xED\xFA\xCE", 4) == 0   /* 32-bit BE. */
            || memcmp(output, "\xFE\xED\xFA\xCF", 4) == 0   /* 64-bit BE. */
            || memcmp(output, "\xCE\xFA\xED\xFE", 4) == 0   /* 32-bit LE. */
            || memcmp(output, "\xCF\xFA\xED\xFE", 4) == 0)  /* 64-bit LE. */
       ) {
        binary_format = CHAZ_CC_BINFMT_MACHO;
    }

    /* Portable Executable. */
    if (binary_format == 0 && output_len >= 0x40
        && memcmp(output, "MZ", 2) == 0
       ) {
        size_t pe_header_off =
            (unsigned char)output[0x3C]
            | ((unsigned char)output[0x3D] << 8)
            | ((unsigned char)output[0x3E] << 16)
            | ((unsigned char)output[0x3F] << 24);

        if (output_len >= pe_header_off + 4
            && memcmp(output + pe_header_off, "PE\0\0", 4) == 0
           ) {
            binary_format = CHAZ_CC_BINFMT_PE;
        }
    }

    chaz_CC.binary_format = binary_format;
    free(output);
}

static const char chaz_CC_eval_macro_code[] =
    CHAZ_QUOTE(  #include <stdio.h>             )
    CHAZ_QUOTE(  int main() {                   )
    CHAZ_QUOTE(  #ifndef %s                     )
    CHAZ_QUOTE(  #error "nope"                  )
    CHAZ_QUOTE(  #endif                         )
    CHAZ_QUOTE(      printf("%%d", %s);         )
    CHAZ_QUOTE(      return 0;                  )
    CHAZ_QUOTE(  }                              );

static int
chaz_CC_eval_macro(const char *macro) {
    size_t size = sizeof(chaz_CC_eval_macro_code)
                  + (strlen(macro) * 2)
                  + 20;
    char *code = (char*)malloc(size);
    int retval = 0;
    char *output;
    size_t len;
    sprintf(code, chaz_CC_eval_macro_code, macro, macro);
    output = chaz_CC_capture_output(code, &len);
    if (output) {
        retval = atoi(output);
        free(output);
    }
    free(code);
    return retval;
}

int
chaz_CC_has_macro(const char *macro) {
    size_t size = sizeof(chaz_CC_eval_macro_code)
                  + (strlen(macro) * 2)
                  + 20;
    char *code = (char*)malloc(size);
    int retval = 0;
    sprintf(code, chaz_CC_eval_macro_code, macro, macro);
    retval = chaz_CC_test_compile(code);
    free(code);
    return retval;
}

static void
chaz_CC_detect_known_compilers(void) {
    chaz_CC.intval___GNUC__  = chaz_CC_eval_macro("__GNUC__");
    if (chaz_CC.intval___GNUC__) {
        chaz_CC.intval___GNUC_MINOR__
            = chaz_CC_eval_macro("__GNUC_MINOR__");
        chaz_CC.intval___GNUC_PATCHLEVEL__
            = chaz_CC_eval_macro("__GNUC_PATCHLEVEL__");
        sprintf(chaz_CC.gcc_version_str, "%d.%d.%d", chaz_CC.intval___GNUC__,
                chaz_CC.intval___GNUC_MINOR__,
                chaz_CC.intval___GNUC_PATCHLEVEL__);
    }
    chaz_CC.intval__MSC_VER   = chaz_CC_eval_macro("_MSC_VER");
    chaz_CC.intval___clang__  = chaz_CC_eval_macro("__clang__");
    chaz_CC.intval___SUNPRO_C = chaz_CC_eval_macro("__SUNPRO_C");
}

void
chaz_CC_clean_up(void) {
    free(chaz_CC.cc_command);
    free(chaz_CC.cflags);
    free(chaz_CC.try_exe_name);
    chaz_CFlags_destroy(chaz_CC.extra_cflags);
    chaz_CFlags_destroy(chaz_CC.temp_cflags);
}

int
chaz_CC_compile_exe(const char *source_path, const char *exe_name,
                    const char *code) {
    chaz_CFlags *local_cflags = chaz_CFlags_new(chaz_CC.cflags_style);
    const char *extra_cflags_string = "";
    const char *temp_cflags_string  = "";
    const char *local_cflags_string;
    char *exe_file = chaz_Util_join("", exe_name, chaz_CC.exe_ext, NULL);
    char *command;
    int result;

    /* Write the source file. */
    chaz_Util_write_file(source_path, code);

    /* Prepare and run the compiler command. */
    if (chaz_CC.extra_cflags) {
        extra_cflags_string = chaz_CFlags_get_string(chaz_CC.extra_cflags);
    }
    if (chaz_CC.temp_cflags) {
        temp_cflags_string = chaz_CFlags_get_string(chaz_CC.temp_cflags);
    }
    chaz_CFlags_set_output_exe(local_cflags, exe_file);
    local_cflags_string = chaz_CFlags_get_string(local_cflags);
    command = chaz_Util_join(" ", chaz_CC.cc_command, chaz_CC.cflags,
                             source_path, extra_cflags_string,
                             temp_cflags_string, local_cflags_string, NULL);
    if (chaz_Util_verbosity < 2) {
        chaz_OS_run_quietly(command);
    }
    else {
        printf("%s\n", command);
        system(command);
    }

    if (chaz_CC.intval__MSC_VER) {
        /* Zap MSVC junk. */
        size_t  junk_buf_size = strlen(exe_file) + 4;
        char   *junk          = (char*)malloc(junk_buf_size);
        sprintf(junk, "%s.obj", exe_name);
        chaz_Util_remove_and_verify(junk);
        sprintf(junk, "%s.ilk", exe_name);
        chaz_Util_remove_and_verify(junk);
        sprintf(junk, "%s.pdb", exe_name);
        chaz_Util_remove_and_verify(junk);
        free(junk);
    }

    /* See if compilation was successful.  Remove the source file. */
    result = chaz_Util_can_open_file(exe_file);
    if (!chaz_Util_remove_and_verify(source_path)) {
        chaz_Util_die("Failed to remove '%s'", source_path);
    }

    chaz_CFlags_destroy(local_cflags);
    free(command);
    free(exe_file);
    return result;
}

int
chaz_CC_compile_obj(const char *source_path, const char *obj_name,
                    const char *code) {
    chaz_CFlags *local_cflags = chaz_CFlags_new(chaz_CC.cflags_style);
    const char *extra_cflags_string = "";
    const char *temp_cflags_string  = "";
    const char *local_cflags_string;
    char *obj_file = chaz_Util_join("", obj_name, chaz_CC.obj_ext, NULL);
    char *command;
    int result;

    /* Write the source file. */
    chaz_Util_write_file(source_path, code);

    /* Prepare and run the compiler command. */
    if (chaz_CC.extra_cflags) {
        extra_cflags_string = chaz_CFlags_get_string(chaz_CC.extra_cflags);
    }
    if (chaz_CC.temp_cflags) {
        temp_cflags_string = chaz_CFlags_get_string(chaz_CC.temp_cflags);
    }
    chaz_CFlags_set_output_obj(local_cflags, obj_file);
    local_cflags_string = chaz_CFlags_get_string(local_cflags);
    command = chaz_Util_join(" ", chaz_CC.cc_command, chaz_CC.cflags,
                             source_path, extra_cflags_string,
                             temp_cflags_string, local_cflags_string, NULL);
    if (chaz_Util_verbosity < 2) {
        chaz_OS_run_quietly(command);
    }
    else {
        printf("%s\n", command);
        system(command);
    }

    /* See if compilation was successful.  Remove the source file. */
    result = chaz_Util_can_open_file(obj_file);
    if (!chaz_Util_remove_and_verify(source_path)) {
        chaz_Util_die("Failed to remove '%s'", source_path);
    }

    chaz_CFlags_destroy(local_cflags);
    free(command);
    free(obj_file);
    return result;
}

int
chaz_CC_test_compile(const char *source) {
    int compile_succeeded;
    char *try_obj_name
        = chaz_Util_join("", CHAZ_CC_TRY_BASENAME, chaz_CC.obj_ext, NULL);
    if (!chaz_Util_remove_and_verify(try_obj_name)) {
        chaz_Util_die("Failed to delete file '%s'", try_obj_name);
    }
    compile_succeeded = chaz_CC_compile_obj(CHAZ_CC_TRY_SOURCE_PATH,
                                            CHAZ_CC_TRY_BASENAME, source);
    chaz_Util_remove_and_verify(try_obj_name);
    free(try_obj_name);
    return compile_succeeded;
}

int
chaz_CC_test_link(const char *source) {
    int link_succeeded;
    if (!chaz_Util_remove_and_verify(chaz_CC.try_exe_name)) {
        chaz_Util_die("Failed to delete file '%s'", chaz_CC.try_exe_name);
    }
    link_succeeded = chaz_CC_compile_exe(CHAZ_CC_TRY_SOURCE_PATH,
                                         CHAZ_CC_TRY_BASENAME, source);
    chaz_Util_remove_and_verify(chaz_CC.try_exe_name);
    return link_succeeded;
}

char*
chaz_CC_capture_output(const char *source, size_t *output_len) {
    char *captured_output = NULL;
    int compile_succeeded;

    /* Clear out previous versions and test to make sure removal worked. */
    if (!chaz_Util_remove_and_verify(chaz_CC.try_exe_name)) {
        chaz_Util_die("Failed to delete file '%s'", chaz_CC.try_exe_name);
    }
    if (!chaz_Util_remove_and_verify(CHAZ_CC_TARGET_PATH)) {
        chaz_Util_die("Failed to delete file '%s'", CHAZ_CC_TARGET_PATH);
    }

    /* Attempt compilation; if successful, run app and slurp output. */
    compile_succeeded = chaz_CC_compile_exe(CHAZ_CC_TRY_SOURCE_PATH,
                                            CHAZ_CC_TRY_BASENAME, source);
    if (compile_succeeded) {
        chaz_OS_run_local_redirected(chaz_CC.try_exe_name,
                                     CHAZ_CC_TARGET_PATH);
        captured_output = chaz_Util_slurp_file(CHAZ_CC_TARGET_PATH,
                                               output_len);
    }
    else {
        *output_len = 0;
    }

    /* Remove all the files we just created. */
    chaz_Util_remove_and_verify(CHAZ_CC_TRY_SOURCE_PATH);
    chaz_Util_remove_and_verify(chaz_CC.try_exe_name);
    chaz_Util_remove_and_verify(CHAZ_CC_TARGET_PATH);

    return captured_output;
}

const char*
chaz_CC_get_cc(void) {
    return chaz_CC.cc_command;
}

const char*
chaz_CC_get_cflags(void) {
    return chaz_CC.cflags;
}

chaz_CFlags*
chaz_CC_get_extra_cflags(void) {
    return chaz_CC.extra_cflags;
}

chaz_CFlags*
chaz_CC_get_temp_cflags(void) {
    return chaz_CC.temp_cflags;
}

chaz_CFlags*
chaz_CC_new_cflags(void) {
    return chaz_CFlags_new(chaz_CC.cflags_style);
}

int
chaz_CC_binary_format(void) {
    return chaz_CC.binary_format;
}

const char*
chaz_CC_exe_ext(void) {
    return chaz_CC.exe_ext;
}

const char*
chaz_CC_shared_lib_ext(void) {
    return chaz_CC.shared_lib_ext;
}

const char*
chaz_CC_static_lib_ext(void) {
    return chaz_CC.static_lib_ext;
}

const char*
chaz_CC_import_lib_ext(void) {
    return chaz_CC.import_lib_ext;
}

const char*
chaz_CC_obj_ext(void) {
    return chaz_CC.obj_ext;
}

int
chaz_CC_gcc_version_num(void) {
    return 10000 * chaz_CC.intval___GNUC__
           + 100 * chaz_CC.intval___GNUC_MINOR__
           + chaz_CC.intval___GNUC_PATCHLEVEL__;
}

const char*
chaz_CC_gcc_version(void) {
    return chaz_CC.intval___GNUC__ ? chaz_CC.gcc_version_str : NULL;
}

int
chaz_CC_msvc_version_num(void) {
    return chaz_CC.intval__MSC_VER;
}

int
chaz_CC_sun_c_version_num(void) {
    return chaz_CC.intval___SUNPRO_C;
}

int
chaz_CC_is_cygwin(void) {
    return chaz_CC.is_cygwin;
}

int
chaz_CC_is_mingw(void) {
    return chaz_CC.is_mingw;
}

const char*
chaz_CC_link_command() {
    if (chaz_CC.intval__MSC_VER) {
        return "link";
    }
    else {
        return chaz_CC.cc_command;
    }
}

char*
chaz_CC_format_archiver_command(const char *target, const char *objects) {
    if (chaz_CC.intval__MSC_VER) {
        /* TODO: Write `objects` to a temporary file in order to avoid
         * exceeding line length limits. */
        char *out = chaz_Util_join("", "/OUT:", target, NULL);
        char *command = chaz_Util_join(" ", "lib", "/NOLOGO", objects, out,
                                       NULL);
        free(out);
        return command;
    }
    else {
        return chaz_Util_join(" ", "ar", "rcs", target, objects, NULL);
    }
}

char*
chaz_CC_format_ranlib_command(const char *target) {
    if (chaz_CC.intval__MSC_VER) {
        return NULL;
    }
    return chaz_Util_join(" ", "ranlib", target, NULL);
}

char*
chaz_CC_shared_lib_filename(const char *dir, const char *basename,
                            const char *version) {
    /* Cygwin uses a "cyg" prefix for shared libraries. */
    const char *prefix = chaz_CC_msvc_version_num()
                         ? ""
                         : chaz_CC_is_cygwin() ? "cyg" : "lib";
    return chaz_CC_build_lib_filename(dir, prefix, basename, version,
                                      chaz_CC.shared_lib_ext);
}

char*
chaz_CC_import_lib_filename(const char *dir, const char *basename,
                            const char *version) {
    const char *prefix = chaz_CC_msvc_version_num() ? "" : "lib";
    return chaz_CC_build_lib_filename(dir, prefix, basename, version,
                                      chaz_CC.import_lib_ext);
}

char*
chaz_CC_export_filename(const char *dir, const char *basename,
                        const char *version) {
    /* Only for MSVC. */
    return chaz_CC_build_lib_filename(dir, "", basename, version, ".exp");
}

static char*
chaz_CC_build_lib_filename(const char *dir, const char *prefix,
                           const char *basename, const char *version,
                           const char *ext) {
    char *suffix;
    char *retval;

    if (version == NULL) {
        suffix = chaz_Util_strdup(ext);
    }
    else {
        int binary_format = chaz_CC_binary_format();

        if (binary_format == CHAZ_CC_BINFMT_PE) {
            suffix = chaz_Util_join("", "-", version, ext, NULL);
        }
        else if (binary_format == CHAZ_CC_BINFMT_MACHO) {
            suffix = chaz_Util_join("", ".", version, ext, NULL);
        }
        else if (binary_format == CHAZ_CC_BINFMT_ELF) {
            suffix = chaz_Util_join("", ext, ".", version, NULL);
        }
        else {
            chaz_Util_die("Unsupported binary format");
            return NULL;
        }
    }

    if (dir == NULL || strcmp(dir, ".") == 0) {
        retval = chaz_Util_join("", prefix, basename, suffix, NULL);
    }
    else {
        const char *dir_sep = chaz_OS_dir_sep();
        retval = chaz_Util_join("", dir, dir_sep, prefix, basename, suffix,
                                NULL);
    }

    free(suffix);
    return retval;
}

char*
chaz_CC_static_lib_filename(const char *dir, const char *basename) {
    const char *prefix = chaz_CC_msvc_version_num() ? "" : "lib";

    if (dir == NULL || strcmp(dir, ".") == 0) {
        return chaz_Util_join("", prefix, basename, chaz_CC.static_lib_ext,
                              NULL);
    }
    else {
        const char *dir_sep = chaz_OS_dir_sep();
        return chaz_Util_join("", dir, dir_sep, prefix, basename,
                              chaz_CC.static_lib_ext, NULL);
    }
}


/***************************************************************************/

#line 17 "src/Charmonizer/Core/ConfWriter.c"
/* #include "Charmonizer/Core/Util.h" */
/* #include "Charmonizer/Core/ConfWriter.h" */
#include <stdarg.h>
#include <stdio.h>

#define CW_MAX_WRITERS 10
static struct {
    chaz_ConfWriter *writers[CW_MAX_WRITERS];
    size_t num_writers;
} chaz_CW;

void
chaz_ConfWriter_init(void) {
    chaz_CW.num_writers = 0;
    return;
}

void
chaz_ConfWriter_clean_up(void) {
    size_t i;
    for (i = 0; i < chaz_CW.num_writers; i++) {
        chaz_CW.writers[i]->clean_up();
    }
}

void
chaz_ConfWriter_append_conf(const char *fmt, ...) {
    va_list args;
    size_t i;
    
    for (i = 0; i < chaz_CW.num_writers; i++) {
        va_start(args, fmt);
        chaz_CW.writers[i]->vappend_conf(fmt, args);
        va_end(args);
    }
}

void
chaz_ConfWriter_add_def(const char *sym, const char *value) {
    size_t i;
    for (i = 0; i < chaz_CW.num_writers; i++) {
        chaz_CW.writers[i]->add_def(sym, value);
    }
}

void
chaz_ConfWriter_add_global_def(const char *sym, const char *value) {
    size_t i;
    for (i = 0; i < chaz_CW.num_writers; i++) {
        chaz_CW.writers[i]->add_global_def(sym, value);
    }
}

void
chaz_ConfWriter_add_typedef(const char *type, const char *alias) {
    size_t i;
    for (i = 0; i < chaz_CW.num_writers; i++) {
        chaz_CW.writers[i]->add_typedef(type, alias);
    }
}

void
chaz_ConfWriter_add_global_typedef(const char *type, const char *alias) {
    size_t i;
    for (i = 0; i < chaz_CW.num_writers; i++) {
        chaz_CW.writers[i]->add_global_typedef(type, alias);
    }
}

void
chaz_ConfWriter_add_sys_include(const char *header) {
    size_t i;
    for (i = 0; i < chaz_CW.num_writers; i++) {
        chaz_CW.writers[i]->add_sys_include(header);
    }
}

void
chaz_ConfWriter_add_local_include(const char *header) {
    size_t i;
    for (i = 0; i < chaz_CW.num_writers; i++) {
        chaz_CW.writers[i]->add_local_include(header);
    }
}

void
chaz_ConfWriter_start_module(const char *module_name) {
    size_t i;
    if (chaz_Util_verbosity > 0) {
        printf("Running %s module...\n", module_name);
    }
    for (i = 0; i < chaz_CW.num_writers; i++) {
        chaz_CW.writers[i]->start_module(module_name);
    }
}

void
chaz_ConfWriter_end_module(void) {
    size_t i;
    for (i = 0; i < chaz_CW.num_writers; i++) {
        chaz_CW.writers[i]->end_module();
    }
}

void
chaz_ConfWriter_add_writer(chaz_ConfWriter *writer) {
    chaz_CW.writers[chaz_CW.num_writers] = writer;
    chaz_CW.num_writers++;
}


/***************************************************************************/

#line 17 "src/Charmonizer/Core/ConfWriterC.c"
/* #include "Charmonizer/Core/Util.h" */
/* #include "Charmonizer/Core/ConfWriter.h" */
/* #include "Charmonizer/Core/ConfWriterC.h" */
/* #include "Charmonizer/Core/OperatingSystem.h" */
/* #include "Charmonizer/Core/Compiler.h" */
#include <ctype.h>
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef enum chaz_ConfElemType {
    CHAZ_CONFELEM_DEF,
    CHAZ_CONFELEM_GLOBAL_DEF,
    CHAZ_CONFELEM_TYPEDEF,
    CHAZ_CONFELEM_GLOBAL_TYPEDEF,
    CHAZ_CONFELEM_SYS_INCLUDE,
    CHAZ_CONFELEM_LOCAL_INCLUDE
} chaz_ConfElemType;

typedef struct chaz_ConfElem {
    char *str1;
    char *str2;
    chaz_ConfElemType type;
} chaz_ConfElem;

/* Static vars. */
static struct {
    FILE          *fh;
    char          *MODULE_NAME;
    chaz_ConfElem *defs;
    size_t         def_cap;
    size_t         def_count;
} chaz_ConfWriterC = { NULL, NULL, NULL, 0, 0 };
static chaz_ConfWriter CWC_conf_writer;

/* Open the charmony.h file handle.  Print supplied text to it, if non-null.
 * Print an explanatory comment and open the include guard.
 */
static void
chaz_ConfWriterC_open_charmony_h(const char *charmony_start);

/* Push a new elem onto the def list. */
static void
chaz_ConfWriterC_push_def_list_item(const char *str1, const char *str2,
                     chaz_ConfElemType type);

/* Free the def list. */
static void
chaz_ConfWriterC_clear_def_list(void);

static void
chaz_ConfWriterC_clean_up(void);
static void
chaz_ConfWriterC_vappend_conf(const char *fmt, va_list args);
static void
chaz_ConfWriterC_add_def(const char *sym, const char *value);
static void
chaz_ConfWriterC_add_global_def(const char *sym, const char *value);
static void
chaz_ConfWriterC_add_typedef(const char *type, const char *alias);
static void
chaz_ConfWriterC_add_global_typedef(const char *type, const char *alias);
static void
chaz_ConfWriterC_add_sys_include(const char *header);
static void
chaz_ConfWriterC_add_local_include(const char *header);
static void
chaz_ConfWriterC_start_module(const char *module_name);
static void
chaz_ConfWriterC_end_module(void);

void
chaz_ConfWriterC_enable(void) {
    CWC_conf_writer.clean_up           = chaz_ConfWriterC_clean_up;
    CWC_conf_writer.vappend_conf       = chaz_ConfWriterC_vappend_conf;
    CWC_conf_writer.add_def            = chaz_ConfWriterC_add_def;
    CWC_conf_writer.add_global_def     = chaz_ConfWriterC_add_global_def;
    CWC_conf_writer.add_typedef        = chaz_ConfWriterC_add_typedef;
    CWC_conf_writer.add_global_typedef = chaz_ConfWriterC_add_global_typedef;
    CWC_conf_writer.add_sys_include    = chaz_ConfWriterC_add_sys_include;
    CWC_conf_writer.add_local_include  = chaz_ConfWriterC_add_local_include;
    CWC_conf_writer.start_module       = chaz_ConfWriterC_start_module;
    CWC_conf_writer.end_module         = chaz_ConfWriterC_end_module;
    chaz_ConfWriterC_open_charmony_h(NULL);
    chaz_ConfWriter_add_writer(&CWC_conf_writer);
    return;
}

static void
chaz_ConfWriterC_open_charmony_h(const char *charmony_start) {
    /* Open the filehandle. */
    chaz_ConfWriterC.fh = fopen("charmony.h", "w+");
    if (chaz_ConfWriterC.fh == NULL) {
        chaz_Util_die("Can't open 'charmony.h': %s", strerror(errno));
    }

    /* Print supplied text (if any) along with warning, open include guard. */
    if (charmony_start != NULL) {
        fwrite(charmony_start, sizeof(char), strlen(charmony_start),
               chaz_ConfWriterC.fh);
    }
    fprintf(chaz_ConfWriterC.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"
           );
}

static void
chaz_ConfWriterC_clean_up(void) {
    /* Write the last bit of charmony.h and close. */
    fprintf(chaz_ConfWriterC.fh, "#endif /* H_CHARMONY */\n\n");
    if (fclose(chaz_ConfWriterC.fh)) {
        chaz_Util_die("Couldn't close 'charmony.h': %s", strerror(errno));
    }
}

static void
chaz_ConfWriterC_vappend_conf(const char *fmt, va_list args) {
    vfprintf(chaz_ConfWriterC.fh, fmt, args);
}

static int
chaz_ConfWriterC_sym_is_uppercase(const char *sym) {
    return isupper((unsigned char)sym[0]);
}

static char*
chaz_ConfWriterC_uppercase_string(const char *src) {
    char *retval = chaz_Util_strdup(src);
    size_t i;
    for (i = 0; retval[i]; ++i) {
        retval[i] = toupper((unsigned char)retval[i]);
    }
    return retval;
}

static void
chaz_ConfWriterC_add_def(const char *sym, const char *value) {
    chaz_ConfWriterC_push_def_list_item(sym, value, CHAZ_CONFELEM_DEF);
}

static void
chaz_ConfWriterC_append_def_to_conf(const char *sym, const char *value) {
    if (value) {
        if (chaz_ConfWriterC_sym_is_uppercase(sym)) {
            fprintf(chaz_ConfWriterC.fh, "#define CHY_%s %s\n", sym, value);
        }
        else {
            fprintf(chaz_ConfWriterC.fh, "#define chy_%s %s\n", sym, value);
        }
    }
    else {
        if (chaz_ConfWriterC_sym_is_uppercase(sym)) {
            fprintf(chaz_ConfWriterC.fh, "#define CHY_%s\n", sym);
        }
        else {
            fprintf(chaz_ConfWriterC.fh, "#define chy_%s\n", sym);
        }
    }
}

static void
chaz_ConfWriterC_add_global_def(const char *sym, const char *value) {
    chaz_ConfWriterC_push_def_list_item(sym, value, CHAZ_CONFELEM_GLOBAL_DEF);
}

static void
chaz_ConfWriterC_append_global_def_to_conf(const char *sym,
                                           const char *value) {
    char *name_end = strchr(sym, '(');
    if (name_end == NULL) {
        if (strcmp(sym, value) == 0) { return; }
        fprintf(chaz_ConfWriterC.fh, "#ifndef %s\n", sym);
    }
    else {
        size_t  name_len = (size_t)(name_end - sym);
        char   *name     = chaz_Util_strdup(sym);
        name[name_len] = '\0';
        fprintf(chaz_ConfWriterC.fh, "#ifndef %s\n", name);
        free(name);
    }
    if (value) {
        fprintf(chaz_ConfWriterC.fh, "  #define %s %s\n", sym, value);
    }
    else {
        fprintf(chaz_ConfWriterC.fh, "  #define %s\n", sym);
    }
    fprintf(chaz_ConfWriterC.fh, "#endif\n");
}

static void
chaz_ConfWriterC_add_typedef(const char *type, const char *alias) {
    chaz_ConfWriterC_push_def_list_item(alias, type, CHAZ_CONFELEM_TYPEDEF);
}

static void
chaz_ConfWriterC_append_typedef_to_conf(const char *type, const char *alias) {
    if (chaz_ConfWriterC_sym_is_uppercase(alias)) {
        fprintf(chaz_ConfWriterC.fh, "typedef %s CHY_%s;\n", type, alias);
    }
    else {
        fprintf(chaz_ConfWriterC.fh, "typedef %s chy_%s;\n", type, alias);
    }
}

static void
chaz_ConfWriterC_add_global_typedef(const char *type, const char *alias) {
    chaz_ConfWriterC_push_def_list_item(alias, type,
            CHAZ_CONFELEM_GLOBAL_TYPEDEF);
}

static void
chaz_ConfWriterC_append_global_typedef_to_conf(const char *type,
                                               const char *alias) {
    if (strcmp(type, alias) == 0) { return; }
    fprintf(chaz_ConfWriterC.fh, "typedef %s %s;\n", type, alias);
}

static void
chaz_ConfWriterC_add_sys_include(const char *header) {
    chaz_ConfWriterC_push_def_list_item(header, NULL,
                                        CHAZ_CONFELEM_SYS_INCLUDE);
}

static void
chaz_ConfWriterC_append_sys_include_to_conf(const char *header) {
    fprintf(chaz_ConfWriterC.fh, "#include <%s>\n", header);
}

static void
chaz_ConfWriterC_add_local_include(const char *header) {
    chaz_ConfWriterC_push_def_list_item(header, NULL,
                                        CHAZ_CONFELEM_LOCAL_INCLUDE);
}

static void
chaz_ConfWriterC_append_local_include_to_conf(const char *header) {
    fprintf(chaz_ConfWriterC.fh, "#include \"%s\"\n", header);
}

static void
chaz_ConfWriterC_start_module(const char *module_name) {
    fprintf(chaz_ConfWriterC.fh, "\n/* %s */\n", module_name);
    chaz_ConfWriterC.MODULE_NAME
        = chaz_ConfWriterC_uppercase_string(module_name);
}

static void
chaz_ConfWriterC_end_module(void) {
    size_t num_globals = 0;
    size_t i;
    chaz_ConfElem *defs = chaz_ConfWriterC.defs;
    for (i = 0; i < chaz_ConfWriterC.def_count; i++) {
        switch (defs[i].type) {
            case CHAZ_CONFELEM_GLOBAL_DEF:
                ++num_globals;
            /* fall through */
            case CHAZ_CONFELEM_DEF:
                chaz_ConfWriterC_append_def_to_conf(defs[i].str1,
                                                    defs[i].str2);
                break;
            case CHAZ_CONFELEM_GLOBAL_TYPEDEF: {
                char *sym = chaz_ConfWriterC_uppercase_string(defs[i].str1);
                chaz_ConfWriterC_append_def_to_conf(sym, defs[i].str2);
                free(sym);
                ++num_globals;
            }
            /* fall through */
            case CHAZ_CONFELEM_TYPEDEF:
                chaz_ConfWriterC_append_typedef_to_conf(defs[i].str2,
                                                        defs[i].str1);
                break;
            case CHAZ_CONFELEM_SYS_INCLUDE:
                ++num_globals;
                break;
            case CHAZ_CONFELEM_LOCAL_INCLUDE:
                chaz_ConfWriterC_append_local_include_to_conf(defs[i].str1);
                break;
            default:
                chaz_Util_die("Internal error: bad element type %d",
                              (int)defs[i].type);
        }
    }

    /* Write out short names. */
    if (chaz_ConfWriterC.def_count > 0) {
        fprintf(chaz_ConfWriterC.fh,
            "\n#if defined(CHY_USE_SHORT_NAMES) "
            "|| defined(CHAZ_USE_SHORT_NAMES)\n"
        );
        for (i = 0; i < chaz_ConfWriterC.def_count; i++) {
            switch (defs[i].type) {
                case CHAZ_CONFELEM_DEF:
                case CHAZ_CONFELEM_TYPEDEF:
                    {
                        const char *sym = defs[i].str1;
                        const char *value = defs[i].str2;
                        if (!value || strcmp(sym, value) != 0) {
                            const char *prefix
                                = chaz_ConfWriterC_sym_is_uppercase(sym)
                                  ? "CHY_" : "chy_";
                            fprintf(chaz_ConfWriterC.fh, "  #define %s %s%s\n",
                                    sym, prefix, sym);
                        }
                    }
                    break;
                case CHAZ_CONFELEM_GLOBAL_DEF:
                case CHAZ_CONFELEM_GLOBAL_TYPEDEF:
                case CHAZ_CONFELEM_SYS_INCLUDE:
                case CHAZ_CONFELEM_LOCAL_INCLUDE:
                    /* no-op */
                    break;
                default:
                    chaz_Util_die("Internal error: bad element type %d",
                                  (int)defs[i].type);
            }
        }

        fprintf(chaz_ConfWriterC.fh, "#endif /* USE_SHORT_NAMES */\n");
    }

    /* Write out global definitions and system includes. */
    if (num_globals) {
        fprintf(chaz_ConfWriterC.fh, "\n#ifdef CHY_EMPLOY_%s\n\n",
                chaz_ConfWriterC.MODULE_NAME);
        for (i = 0; i < chaz_ConfWriterC.def_count; i++) {
            switch (defs[i].type) {
                case CHAZ_CONFELEM_GLOBAL_DEF:
                    chaz_ConfWriterC_append_global_def_to_conf(defs[i].str1,
                                                               defs[i].str2);
                    break;
                case CHAZ_CONFELEM_GLOBAL_TYPEDEF:
                    chaz_ConfWriterC_append_global_typedef_to_conf(
                            defs[i].str2, defs[i].str1);
                    break;
                case CHAZ_CONFELEM_SYS_INCLUDE:
                    chaz_ConfWriterC_append_sys_include_to_conf(defs[i].str1);
                    break;
                case CHAZ_CONFELEM_DEF:
                case CHAZ_CONFELEM_TYPEDEF:
                case CHAZ_CONFELEM_LOCAL_INCLUDE:
                    /* no-op */
                    break;
                default:
                    chaz_Util_die("Internal error: bad element type %d",
                                  (int)defs[i].type);
            }
        }
        fprintf(chaz_ConfWriterC.fh, "\n#endif /* EMPLOY_%s */\n",
                chaz_ConfWriterC.MODULE_NAME);
    }

    fprintf(chaz_ConfWriterC.fh, "\n");

    free(chaz_ConfWriterC.MODULE_NAME);
    chaz_ConfWriterC_clear_def_list();
}

static void
chaz_ConfWriterC_push_def_list_item(const char *str1, const char *str2,
                     chaz_ConfElemType type) {
    if (chaz_ConfWriterC.def_count >= chaz_ConfWriterC.def_cap) { 
        size_t amount;
        chaz_ConfWriterC.def_cap += 10;
        amount = chaz_ConfWriterC.def_cap * sizeof(chaz_ConfElem);
        chaz_ConfWriterC.defs
            = (chaz_ConfElem*)realloc(chaz_ConfWriterC.defs, amount);
    }
    chaz_ConfWriterC.defs[chaz_ConfWriterC.def_count].str1
        = str1 ? chaz_Util_strdup(str1) : NULL;
    chaz_ConfWriterC.defs[chaz_ConfWriterC.def_count].str2
        = str2 ? chaz_Util_strdup(str2) : NULL;
    chaz_ConfWriterC.defs[chaz_ConfWriterC.def_count].type = type;
    chaz_ConfWriterC.def_count++;
}

static void
chaz_ConfWriterC_clear_def_list(void) {
    size_t i;
    for (i = 0; i < chaz_ConfWriterC.def_count; i++) {
        free(chaz_ConfWriterC.defs[i].str1);
        free(chaz_ConfWriterC.defs[i].str2);
    }
    free(chaz_ConfWriterC.defs);
    chaz_ConfWriterC.defs      = NULL;
    chaz_ConfWriterC.def_cap   = 0;
    chaz_ConfWriterC.def_count = 0;
}


/***************************************************************************/

#line 17 "src/Charmonizer/Core/ConfWriterPerl.c"
/* #include "Charmonizer/Core/Util.h" */
/* #include "Charmonizer/Core/ConfWriter.h" */
/* #include "Charmonizer/Core/ConfWriterPerl.h" */
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* Static vars. */
static struct {
    FILE *fh;
} chaz_CWPerl = { NULL };
static chaz_ConfWriter CWPerl_conf_writer;

/* Open the Charmony.pm file handle.
 */
static void
chaz_ConfWriterPerl_open_config_pm(void);

static void
chaz_ConfWriterPerl_clean_up(void);
static void
chaz_ConfWriterPerl_vappend_conf(const char *fmt, va_list args);
static void
chaz_ConfWriterPerl_add_def(const char *sym, const char *value);
static void
chaz_ConfWriterPerl_add_global_def(const char *sym, const char *value);
static void
chaz_ConfWriterPerl_add_typedef(const char *type, const char *alias);
static void
chaz_ConfWriterPerl_add_global_typedef(const char *type, const char *alias);
static void
chaz_ConfWriterPerl_add_sys_include(const char *header);
static void
chaz_ConfWriterPerl_add_local_include(const char *header);
static void
chaz_ConfWriterPerl_start_module(const char *module_name);
static void
chaz_ConfWriterPerl_end_module(void);

void
chaz_ConfWriterPerl_enable(void) {
    CWPerl_conf_writer.clean_up           = chaz_ConfWriterPerl_clean_up;
    CWPerl_conf_writer.vappend_conf       = chaz_ConfWriterPerl_vappend_conf;
    CWPerl_conf_writer.add_def            = chaz_ConfWriterPerl_add_def;
    CWPerl_conf_writer.add_global_def     = chaz_ConfWriterPerl_add_global_def;
    CWPerl_conf_writer.add_typedef        = chaz_ConfWriterPerl_add_typedef;
    CWPerl_conf_writer.add_global_typedef = chaz_ConfWriterPerl_add_global_typedef;
    CWPerl_conf_writer.add_sys_include    = chaz_ConfWriterPerl_add_sys_include;
    CWPerl_conf_writer.add_local_include  = chaz_ConfWriterPerl_add_local_include;
    CWPerl_conf_writer.start_module       = chaz_ConfWriterPerl_start_module;
    CWPerl_conf_writer.end_module         = chaz_ConfWriterPerl_end_module;
    chaz_ConfWriterPerl_open_config_pm();
    chaz_ConfWriter_add_writer(&CWPerl_conf_writer);
    return;
}

static void
chaz_ConfWriterPerl_open_config_pm(void) {
    /* Open the filehandle. */
    chaz_CWPerl.fh = fopen("Charmony.pm", "w+");
    if (chaz_CWPerl.fh == NULL) {
        chaz_Util_die("Can't open 'Charmony.pm': %s", strerror(errno));
    }

    /* Start the module. */
    fprintf(chaz_CWPerl.fh,
            "# Auto-generated by Charmonizer. \n"
            "# DO NOT EDIT THIS FILE!!\n"
            "\n"
            "package Charmony;\n"
            "use strict;\n"
            "use warnings;\n"
            "\n"
            "my %%defs;\n"
            "\n"
            "sub config { \\%%defs }\n"
            "\n"
           );
}

static void
chaz_ConfWriterPerl_clean_up(void) {
    /* Write the last bit of Charmony.pm and close. */
    fprintf(chaz_CWPerl.fh, "\n1;\n\n");
    if (fclose(chaz_CWPerl.fh)) {
        chaz_Util_die("Couldn't close 'Charmony.pm': %s", strerror(errno));
    }
}

static void
chaz_ConfWriterPerl_vappend_conf(const char *fmt, va_list args) {
    (void)fmt;
    (void)args;
}

static char*
chaz_ConfWriterPerl_quotify(const char *string, char *buf, size_t buf_size) {
    char *quoted = buf;

    /* Don't bother with undef values here. */
    if (!string) {
        return NULL;
    }

    /* Allocate memory if necessary. */
    {
        const char *ptr;
        size_t space = 3; /* Quotes plus NUL termination. */
        for (ptr = string; *ptr; ptr++) {
            if (*ptr == '\'' || *ptr == '\\') {
                space += 2;
            }
            else {
                space += 1;
            }
        }
        if (space > buf_size) {
            quoted = (char*)malloc(space);
        }
    }

    /* Perform copying and escaping */
    {
        const char *ptr;
        size_t pos = 0;
        quoted[pos++] = '\'';
        for (ptr = string; *ptr; ptr++) {
            if (*ptr == '\'' || *ptr == '\\') {
                quoted[pos++] = '\\';
            }
            quoted[pos++] = *ptr;
        }
        quoted[pos++] = '\'';
        quoted[pos++] = '\0';
    }

    return quoted;
}

#define CFPERL_MAX_BUF 100
static void
chaz_ConfWriterPerl_add_def(const char *sym, const char *value) {
    char sym_buf[CFPERL_MAX_BUF + 1];
    char value_buf[CFPERL_MAX_BUF + 1];
    char *quoted_sym;
    char *quoted_value;

    /* Quote key. */
    if (!sym) {
        chaz_Util_die("Can't handle NULL key");
    }
    quoted_sym = chaz_ConfWriterPerl_quotify(sym, sym_buf, CFPERL_MAX_BUF);

    /* Quote value or use "undef". */
    if (!value) {
        strcpy(value_buf, "undef");
        quoted_value = value_buf;
    }
    else {
        quoted_value = chaz_ConfWriterPerl_quotify(value, value_buf,
                                                CFPERL_MAX_BUF);
    }

    fprintf(chaz_CWPerl.fh, "$defs{%s} = %s;\n", quoted_sym, quoted_value);

    if (quoted_sym   != sym_buf)   { free(quoted_sym);   }
    if (quoted_value != value_buf) { free(quoted_value); }
}

static void
chaz_ConfWriterPerl_add_global_def(const char *sym, const char *value) {
    (void)sym;
    (void)value;
}

static void
chaz_ConfWriterPerl_add_typedef(const char *type, const char *alias) {
    (void)type;
    (void)alias;
}

static void
chaz_ConfWriterPerl_add_global_typedef(const char *type, const char *alias) {
    (void)type;
    (void)alias;
}

static void
chaz_ConfWriterPerl_add_sys_include(const char *header) {
    (void)header;
}

static void
chaz_ConfWriterPerl_add_local_include(const char *header) {
    (void)header;
}

static void
chaz_ConfWriterPerl_start_module(const char *module_name) {
    fprintf(chaz_CWPerl.fh, "# %s\n", module_name);
}

static void
chaz_ConfWriterPerl_end_module(void) {
    fprintf(chaz_CWPerl.fh, "\n");
}


/***************************************************************************/

#line 17 "src/Charmonizer/Core/ConfWriterPython.c"
/* #include "Charmonizer/Core/Util.h" */
/* #include "Charmonizer/Core/ConfWriter.h" */
/* #include "Charmonizer/Core/ConfWriterPython.h" */
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* Static vars. */
static struct {
    FILE *fh;
} chaz_CWPython = { NULL };
static chaz_ConfWriter CWPython_conf_writer;

/* Open the charmony.py file handle.
 */
static void
chaz_ConfWriterPython_open_config_py(void);

static void
chaz_ConfWriterPython_clean_up(void);
static void
chaz_ConfWriterPython_vappend_conf(const char *fmt, va_list args);
static void
chaz_ConfWriterPython_add_def(const char *sym, const char *value);
static void
chaz_ConfWriterPython_add_global_def(const char *sym, const char *value);
static void
chaz_ConfWriterPython_add_typedef(const char *type, const char *alias);
static void
chaz_ConfWriterPython_add_global_typedef(const char *type, const char *alias);
static void
chaz_ConfWriterPython_add_sys_include(const char *header);
static void
chaz_ConfWriterPython_add_local_include(const char *header);
static void
chaz_ConfWriterPython_start_module(const char *module_name);
static void
chaz_ConfWriterPython_end_module(void);

void
chaz_ConfWriterPython_enable(void) {
    CWPython_conf_writer.clean_up           = chaz_ConfWriterPython_clean_up;
    CWPython_conf_writer.vappend_conf       = chaz_ConfWriterPython_vappend_conf;
    CWPython_conf_writer.add_def            = chaz_ConfWriterPython_add_def;
    CWPython_conf_writer.add_global_def     = chaz_ConfWriterPython_add_global_def;
    CWPython_conf_writer.add_typedef        = chaz_ConfWriterPython_add_typedef;
    CWPython_conf_writer.add_global_typedef = chaz_ConfWriterPython_add_global_typedef;
    CWPython_conf_writer.add_sys_include    = chaz_ConfWriterPython_add_sys_include;
    CWPython_conf_writer.add_local_include  = chaz_ConfWriterPython_add_local_include;
    CWPython_conf_writer.start_module       = chaz_ConfWriterPython_start_module;
    CWPython_conf_writer.end_module         = chaz_ConfWriterPython_end_module;
    chaz_ConfWriterPython_open_config_py();
    chaz_ConfWriter_add_writer(&CWPython_conf_writer);
    return;
}

static void
chaz_ConfWriterPython_open_config_py(void) {
    /* Open the filehandle. */
    chaz_CWPython.fh = fopen("charmony.py", "w+");
    if (chaz_CWPython.fh == NULL) {
        chaz_Util_die("Can't open 'charmony.py': %s", strerror(errno));
    }

    /* Start the module. */
    fprintf(chaz_CWPython.fh,
            "# Auto-generated by Charmonizer. \n"
            "# DO NOT EDIT THIS FILE!!\n"
            "\n"
            "class Charmony(object):\n"
            "    @classmethod\n"
            "    def config(cls):\n"
            "        return cls.defs\n"
            "\n"
            "    defs = {}\n"
            "\n"
           );
}

static void
chaz_ConfWriterPython_clean_up(void) {
    /* No more code necessary to finish charmony.py, so just close. */
    if (fclose(chaz_CWPython.fh)) {
        chaz_Util_die("Couldn't close 'charmony.py': %s", strerror(errno));
    }
}

static void
chaz_ConfWriterPython_vappend_conf(const char *fmt, va_list args) {
    (void)fmt;
    (void)args;
}

static char*
chaz_ConfWriterPython_quotify(const char *string, char *buf, size_t buf_size) {
    char *quoted = buf;

    /* Don't bother with NULL values here. */
    if (!string) {
        return NULL;
    }

    /* Allocate memory if necessary. */
    {
        const char *ptr;
        size_t space = 3; /* Quotes plus NUL termination. */
        for (ptr = string; *ptr; ptr++) {
            if (*ptr == '\'' || *ptr == '\\') {
                space += 2;
            }
            else {
                space += 1;
            }
        }
        if (space > buf_size) {
            quoted = (char*)malloc(space);
        }
    }

    /* Perform copying and escaping */
    {
        const char *ptr;
        size_t pos = 0;
        quoted[pos++] = '\'';
        for (ptr = string; *ptr; ptr++) {
            if (*ptr == '\'' || *ptr == '\\') {
                quoted[pos++] = '\\';
            }
            quoted[pos++] = *ptr;
        }
        quoted[pos++] = '\'';
        quoted[pos++] = '\0';
    }

    return quoted;
}

#define CFPYTHON_MAX_BUF 100
static void
chaz_ConfWriterPython_add_def(const char *sym, const char *value) {
    char sym_buf[CFPYTHON_MAX_BUF + 1];
    char value_buf[CFPYTHON_MAX_BUF + 1];
    char *quoted_sym;
    char *quoted_value;

    /* Quote key. */
    if (!sym) {
        chaz_Util_die("Can't handle NULL key");
    }
    quoted_sym = chaz_ConfWriterPython_quotify(sym, sym_buf, CFPYTHON_MAX_BUF);

    /* Quote value or use "None". */
    if (!value) {
        strcpy(value_buf, "None");
        quoted_value = value_buf;
    }
    else {
        quoted_value = chaz_ConfWriterPython_quotify(value, value_buf,
                                                     CFPYTHON_MAX_BUF);
    }

    fprintf(chaz_CWPython.fh, "    defs[%s] = %s\n", quoted_sym, quoted_value);

    if (quoted_sym   != sym_buf)   { free(quoted_sym);   }
    if (quoted_value != value_buf) { free(quoted_value); }
}

static void
chaz_ConfWriterPython_add_global_def(const char *sym, const char *value) {
    (void)sym;
    (void)value;
}

static void
chaz_ConfWriterPython_add_typedef(const char *type, const char *alias) {
    (void)type;
    (void)alias;
}

static void
chaz_ConfWriterPython_add_global_typedef(const char *type, const char *alias) {
    (void)type;
    (void)alias;
}

static void
chaz_ConfWriterPython_add_sys_include(const char *header) {
    (void)header;
}

static void
chaz_ConfWriterPython_add_local_include(const char *header) {
    (void)header;
}

static void
chaz_ConfWriterPython_start_module(const char *module_name) {
    fprintf(chaz_CWPython.fh, "    # %s\n", module_name);
}

static void
chaz_ConfWriterPython_end_module(void) {
    fprintf(chaz_CWPython.fh, "\n");
}


/***************************************************************************/

#line 17 "src/Charmonizer/Core/ConfWriterRuby.c"
/* #include "Charmonizer/Core/Util.h" */
/* #include "Charmonizer/Core/ConfWriter.h" */
/* #include "Charmonizer/Core/ConfWriterRuby.h" */
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* Static vars. */
static struct {
    FILE *fh;
} chaz_CWRuby = { NULL };
static chaz_ConfWriter CWRuby_conf_writer;

/* Open the Charmony.rb file handle.
 */
static void
chaz_ConfWriterRuby_open_config_rb(void);

static void
chaz_ConfWriterRuby_clean_up(void);
static void
chaz_ConfWriterRuby_vappend_conf(const char *fmt, va_list args);
static void
chaz_ConfWriterRuby_add_def(const char *sym, const char *value);
static void
chaz_ConfWriterRuby_add_global_def(const char *sym, const char *value);
static void
chaz_ConfWriterRuby_add_typedef(const char *type, const char *alias);
static void
chaz_ConfWriterRuby_add_global_typedef(const char *type, const char *alias);
static void
chaz_ConfWriterRuby_add_sys_include(const char *header);
static void
chaz_ConfWriterRuby_add_local_include(const char *header);
static void
chaz_ConfWriterRuby_start_module(const char *module_name);
static void
chaz_ConfWriterRuby_end_module(void);

void
chaz_ConfWriterRuby_enable(void) {
    CWRuby_conf_writer.clean_up           = chaz_ConfWriterRuby_clean_up;
    CWRuby_conf_writer.vappend_conf       = chaz_ConfWriterRuby_vappend_conf;
    CWRuby_conf_writer.add_def            = chaz_ConfWriterRuby_add_def;
    CWRuby_conf_writer.add_global_def     = chaz_ConfWriterRuby_add_global_def;
    CWRuby_conf_writer.add_typedef        = chaz_ConfWriterRuby_add_typedef;
    CWRuby_conf_writer.add_global_typedef = chaz_ConfWriterRuby_add_global_typedef;
    CWRuby_conf_writer.add_sys_include    = chaz_ConfWriterRuby_add_sys_include;
    CWRuby_conf_writer.add_local_include  = chaz_ConfWriterRuby_add_local_include;
    CWRuby_conf_writer.start_module       = chaz_ConfWriterRuby_start_module;
    CWRuby_conf_writer.end_module         = chaz_ConfWriterRuby_end_module;
    chaz_ConfWriterRuby_open_config_rb();
    chaz_ConfWriter_add_writer(&CWRuby_conf_writer);
    return;
}

static void
chaz_ConfWriterRuby_open_config_rb(void) {
    /* Open the filehandle. */
    chaz_CWRuby.fh = fopen("Charmony.rb", "w+");
    if (chaz_CWRuby.fh == NULL) {
        chaz_Util_die("Can't open 'Charmony.rb': %s", strerror(errno));
    }

    /* Start the module. */
    fprintf(chaz_CWRuby.fh,
            "# Auto-generated by Charmonizer. \n"
            "# DO NOT EDIT THIS FILE!!\n"
            "\n"
            "module Charmony\n"
            "\n"
            "defs = {}\n"
            "\n"
            "def config\ndefs\nend\n"
            "\n"
           );
}

static void
chaz_ConfWriterRuby_clean_up(void) {
    /* Write the last bit of Charmony.rb and close. */
    fprintf(chaz_CWRuby.fh, "\nend\n\n");
    if (fclose(chaz_CWRuby.fh)) {
        chaz_Util_die("Couldn't close 'Charmony.rb': %s", strerror(errno));
    }
}

static void
chaz_ConfWriterRuby_vappend_conf(const char *fmt, va_list args) {
    (void)fmt;
    (void)args;
}

static char*
chaz_ConfWriterRuby_quotify(const char *string, char *buf, size_t buf_size) {
    char *quoted = buf;

    /* Don't bother with undef values here. */
    if (!string) {
        return NULL;
    }

    /* Allocate memory if necessary. */
    {
        const char *ptr;
        size_t space = 3; /* Quotes plus NUL termination. */
        for (ptr = string; *ptr; ptr++) {
            if (*ptr == '\'' || *ptr == '\\') {
                space += 2;
            }
            else {
                space += 1;
            }
        }
        if (space > buf_size) {
            quoted = (char*)malloc(space);
        }
    }

    /* Perform copying and escaping */
    {
        const char *ptr;
        size_t pos = 0;
        quoted[pos++] = '\'';
        for (ptr = string; *ptr; ptr++) {
            if (*ptr == '\'' || *ptr == '\\') {
                quoted[pos++] = '\\';
            }
            quoted[pos++] = *ptr;
        }
        quoted[pos++] = '\'';
        quoted[pos++] = '\0';
    }

    return quoted;
}

#define CFRUBY_MAX_BUF 100
static void
chaz_ConfWriterRuby_add_def(const char *sym, const char *value) {
    char sym_buf[CFRUBY_MAX_BUF + 1];
    char value_buf[CFRUBY_MAX_BUF + 1];
    char *quoted_sym;
    char *quoted_value;

    /* Quote key. */
    if (!sym) {
        chaz_Util_die("Can't handle NULL key");
    }
    quoted_sym = chaz_ConfWriterRuby_quotify(sym, sym_buf, CFRUBY_MAX_BUF);

    /* Quote value or use "nil". */
    if (!value) {
        strcpy(value_buf, "nil");
        quoted_value = value_buf;
    }
    else {
        quoted_value = chaz_ConfWriterRuby_quotify(value, value_buf,
                                                CFRUBY_MAX_BUF);
    }

    fprintf(chaz_CWRuby.fh, "defs[%s] = %s\n", quoted_sym, quoted_value);

    if (quoted_sym   != sym_buf)   { free(quoted_sym);   }
    if (quoted_value != value_buf) { free(quoted_value); }
}

static void
chaz_ConfWriterRuby_add_global_def(const char *sym, const char *value) {
    (void)sym;
    (void)value;
}

static void
chaz_ConfWriterRuby_add_typedef(const char *type, const char *alias) {
    (void)type;
    (void)alias;
}

static void
chaz_ConfWriterRuby_add_global_typedef(const char *type, const char *alias) {
    (void)type;
    (void)alias;
}

static void
chaz_ConfWriterRuby_add_sys_include(const char *header) {
    (void)header;
}

static void
chaz_ConfWriterRuby_add_local_include(const char *header) {
    (void)header;
}

static void
chaz_ConfWriterRuby_start_module(const char *module_name) {
    fprintf(chaz_CWRuby.fh, "# %s\n", module_name);
}

static void
chaz_ConfWriterRuby_end_module(void) {
    fprintf(chaz_CWRuby.fh, "\n");
}


/***************************************************************************/

#line 17 "src/Charmonizer/Core/HeaderChecker.c"
/* #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 chaz_CHeader {
    const char  *name;
    int          exists;
} chaz_CHeader;

/* Keep a sorted, dynamically-sized array of names of all headers we've
 * checked for so far.
 */
static struct {
    int            cache_size;
    chaz_CHeader **header_cache;
} chaz_HeadCheck = { 0, NULL };

/* Comparison function to feed to qsort, bsearch, etc.
 */
static int
chaz_HeadCheck_compare_headers(const void *vptr_a, const void *vptr_b);

/* Run a test compilation and return a new chaz_CHeader object encapsulating
 * the results.
 */
static chaz_CHeader*
chaz_HeadCheck_discover_header(const char *header_name);

/* Extend the cache, add this chaz_CHeader object to it, and sort.
 */
static void
chaz_HeadCheck_add_to_cache(chaz_CHeader *header);

/* Like add_to_cache, but takes a individual elements instead of a
 * chaz_CHeader* and checks if header exists in array first.
 */
static void
chaz_HeadCheck_maybe_add_to_cache(const char *header_name, int exists);

void
chaz_HeadCheck_init(void) {
    chaz_CHeader *null_header = (chaz_CHeader*)malloc(sizeof(chaz_CHeader));

    /* Create terminating record for the dynamic array of chaz_CHeader
     * objects. */
    null_header->name   = NULL;
    null_header->exists = false;
    chaz_HeadCheck.header_cache    = (chaz_CHeader**)malloc(sizeof(void*));
    *(chaz_HeadCheck.header_cache) = null_header;
    chaz_HeadCheck.cache_size = 1;
}

int
chaz_HeadCheck_check_header(const char *header_name) {
    chaz_CHeader  *header;
    chaz_CHeader   key;
    chaz_CHeader  *fake = &key;
    chaz_CHeader **header_ptr;

    /* Fake up a key to feed to bsearch; see if the header's already there. */
    key.name = header_name;
    key.exists = false;
    header_ptr = (chaz_CHeader**)bsearch(&fake, chaz_HeadCheck.header_cache,
                                         chaz_HeadCheck.cache_size,
                                         sizeof(void*),
                                         chaz_HeadCheck_compare_headers);

    /* If it's not there, go try a test compile. */
    if (header_ptr == NULL) {
        header = chaz_HeadCheck_discover_header(header_name);
        chaz_HeadCheck_add_to_cache(header);
    }
    else {
        header = *header_ptr;
    }

    return header->exists;
}

int
chaz_HeadCheck_check_many_headers(const char **header_names) {
    static const char test_code[] = "int main() { return 0; }\n";
    int success;
    int i;
    char *code_buf;
    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 = chaz_CC_test_compile(code_buf);
    if (success) {
        for (i = 0; header_names[i] != NULL; i++) {
            chaz_HeadCheck_maybe_add_to_cache(header_names[i], true);
        }
    }

    free(code_buf);
    return success;
}

int
chaz_HeadCheck_defines_symbol(const char *symbol, const char *includes) {
    /*
     * Casting function pointers to object pointers like 'char*' is a C
     * extension, so for a bullet-proof check, a separate test for functions
     * might be necessary.
     */
    static const char defines_code[] =
        CHAZ_QUOTE(  %s                                            )
        CHAZ_QUOTE(  int main() {                                  )
        CHAZ_QUOTE(  #ifdef %s                                     )
        CHAZ_QUOTE(      return 0;                                 )
        CHAZ_QUOTE(  #else                                         )
        CHAZ_QUOTE(      return *(char*)&%s;                       )
        CHAZ_QUOTE(  #endif                                        )
        CHAZ_QUOTE(  }                                             );
    long needed = sizeof(defines_code)
                  + 2 * strlen(symbol)
                  + strlen(includes)
                  + 10;
    char *buf = (char*)malloc(needed);
    int retval;
    sprintf(buf, defines_code, includes, symbol, symbol);
    retval = chaz_CC_test_compile(buf);
    free(buf);
    return retval;
}

int
chaz_HeadCheck_contains_member(const char *struct_name, const char *member,
                               const char *includes) {
    static const char contains_code[] =
        CHAZ_QUOTE(  #include <stddef.h>                           )
        CHAZ_QUOTE(  %s                                            )
        CHAZ_QUOTE(  int main() { return offsetof(%s, %s); }       );
    long needed = sizeof(contains_code)
                  + strlen(struct_name)
                  + strlen(member)
                  + strlen(includes)
                  + 10;
    char *buf = (char*)malloc(needed);
    int retval;
    sprintf(buf, contains_code, includes, struct_name, member);
    retval = chaz_CC_test_compile(buf);
    free(buf);
    return retval;
}

static int
chaz_HeadCheck_compare_headers(const void *vptr_a, const void *vptr_b) {
    chaz_CHeader *const *const a = (chaz_CHeader*const*)vptr_a;
    chaz_CHeader *const *const b = (chaz_CHeader*const*)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 chaz_CHeader*
chaz_HeadCheck_discover_header(const char *header_name) {
    static const char test_code[] = "int main() { return 0; }\n";
    chaz_CHeader* header = (chaz_CHeader*)malloc(sizeof(chaz_CHeader));
    size_t  needed = strlen(header_name) + sizeof(test_code) + 50;
    char *include_test = (char*)malloc(needed);

    /* Assign. */
    header->name = chaz_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 = chaz_CC_test_compile(include_test);

    free(include_test);
    return header;
}

static void
chaz_HeadCheck_add_to_cache(chaz_CHeader *header) {
    size_t amount;

    /* Realloc array -- inefficient, but this isn't a bottleneck. */
    amount = ++chaz_HeadCheck.cache_size * sizeof(void*);
    chaz_HeadCheck.header_cache
        = (chaz_CHeader**)realloc(chaz_HeadCheck.header_cache, amount);
    chaz_HeadCheck.header_cache[chaz_HeadCheck.cache_size - 1] = header;

    /* Keep the list of headers sorted. */
    qsort(chaz_HeadCheck.header_cache, chaz_HeadCheck.cache_size,
          sizeof(*(chaz_HeadCheck.header_cache)),
          chaz_HeadCheck_compare_headers);
}

static void
chaz_HeadCheck_maybe_add_to_cache(const char *header_name, int exists) {
    chaz_CHeader *header;
    chaz_CHeader  key;
    chaz_CHeader *fake = &key;

    /* Fake up a key and bsearch for it. */
    key.name   = header_name;
    key.exists = exists;
    header = (chaz_CHeader*)bsearch(&fake, chaz_HeadCheck.header_cache,
                                    chaz_HeadCheck.cache_size, sizeof(void*),
                                    chaz_HeadCheck_compare_headers);

    /* We've already done the test compile, so skip that step and add it. */
    if (header == NULL) {
        header = (chaz_CHeader*)malloc(sizeof(chaz_CHeader));
        header->name   = chaz_Util_strdup(header_name);
        header->exists = exists;
        chaz_HeadCheck_add_to_cache(header);
    }
}



/***************************************************************************/

#line 17 "src/Charmonizer/Core/Make.c"
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
/* #include "Charmonizer/Core/Make.h" */
/* #include "Charmonizer/Core/CFlags.h" */
/* #include "Charmonizer/Core/Compiler.h" */
/* #include "Charmonizer/Core/OperatingSystem.h" */
/* #include "Charmonizer/Core/Util.h" */

#define CHAZ_MAKEBINARY_EXE         1
#define CHAZ_MAKEBINARY_STATIC_LIB  2
#define CHAZ_MAKEBINARY_SHARED_LIB  3

struct chaz_MakeVar {
    char   *name;
    char   *value;
    size_t  num_elements;
};

struct chaz_MakeRule {
    char *targets;
    char *prereqs;
    char *commands;
};

struct chaz_MakeBinary {
    int             type;
    char           *target_dir;
    char           *basename;
    char           *version;
    char           *major_version;
    char          **sources;  /* List of all sources. */
    size_t          num_sources;
    char          **single_sources;  /* Only sources from add_src_file. */
    size_t          num_single_sources;
    char          **dirs;
    size_t          num_dirs;
    chaz_MakeVar   *obj_var;  /* Owned by MakeFile. */
    char           *dollar_var;
    chaz_MakeRule  *rule;  /* Not added to MakeFile, owned by MakeBinary. */
    chaz_CFlags    *compile_flags;
    chaz_CFlags    *link_flags;
};

struct chaz_MakeFile {
    chaz_MakeVar    **vars;
    size_t            num_vars;
    chaz_MakeRule   **rules;
    size_t            num_rules;
    chaz_MakeRule    *clean;
    chaz_MakeRule    *distclean;
    chaz_MakeBinary **binaries;
    size_t            num_binaries;
};

typedef struct {
    chaz_MakeBinary         *binary;
    chaz_Make_file_filter_t  filter;
    void                    *filter_ctx;
} chaz_MakeBinaryContext;

/* Static vars. */
static struct {
    char *make_command;
    int   shell_type;
    int   supports_pattern_rules;
} chaz_Make = {
    NULL,
    0, 0
};

/* Detect make command.
 *
 * The argument list must be a NULL-terminated series of different spellings
 * of `make`, which will be auditioned in the order they are supplied.  Here
 * are several possibilities:
 *
 *      make
 *      gmake
 *      nmake
 *      dmake
 */
static int
S_chaz_Make_detect(const char *make1, ...);

static int
S_chaz_Make_audition(const char *make);

static void
S_chaz_MakeFile_finish_exe(chaz_MakeFile *self, chaz_MakeBinary *binary);

static void
S_chaz_MakeFile_finish_shared_lib(chaz_MakeFile *self,
                                  chaz_MakeBinary *binary);

static void
S_chaz_MakeFile_finish_static_lib(chaz_MakeFile *self,
                                  chaz_MakeBinary *binary);

static chaz_MakeBinary*
S_chaz_MakeFile_add_binary(chaz_MakeFile *self, int type, const char *dir,
                           const char *basename, const char *target);

static void
S_chaz_MakeFile_write_binary_rules(chaz_MakeFile *self,
                                   chaz_MakeBinary *binary, FILE *out);

static void
S_chaz_MakeFile_write_object_rules(char **sources, const char *command,
                                   FILE *out);

static void
S_chaz_MakeFile_write_pattern_rules(char **dirs, const char *command,
                                    FILE *out);

static chaz_MakeRule*
S_chaz_MakeRule_new(const char *target, const char *prereq);

static void
S_chaz_MakeRule_destroy(chaz_MakeRule *self);

static void
S_chaz_MakeRule_write(chaz_MakeRule *self, FILE *out);

static void
S_chaz_MakeBinary_destroy(chaz_MakeBinary *self);

static void
S_chaz_MakeBinary_list_files_callback(const char *dir, char *file,
                                      void *context);
static void
S_chaz_MakeBinary_do_add_src_file(chaz_MakeBinary *self, const char *path);

/** Return the path to the object file for a source file.
 *
 * @param path The path to the source file.
 */
static char*
S_chaz_MakeBinary_obj_path(const char *src_path);

void
chaz_Make_init(const char *make_command) {
    chaz_Make.shell_type = chaz_OS_shell_type();

    if (make_command) {
        if (!S_chaz_Make_detect(make_command, NULL)) {
            chaz_Util_warn("Make utility '%s' doesn't appear to work",
                           make_command);
        }
    }
    else {
        int succeeded = 0;

        /* mingw32-make seems to try to run commands under both cmd.exe
         * and sh.exe. Not sure about dmake.
         */
        if (chaz_Make.shell_type == CHAZ_OS_POSIX) {
            succeeded = S_chaz_Make_detect("make", "gmake", "dmake",
                                           "mingw32-make", NULL);
        }
        else if (chaz_Make.shell_type == CHAZ_OS_CMD_EXE) {
            succeeded = S_chaz_Make_detect("nmake", "dmake", "mingw32-make",
                                           NULL);
        }

        if (!succeeded) {
            chaz_Util_warn("No working make utility found");
        }
        else if (chaz_Util_verbosity) {
            printf("Detected make utility '%s'\n", chaz_Make.make_command);
        }
    }
}

void
chaz_Make_clean_up(void) {
    free(chaz_Make.make_command);
}

const char*
chaz_Make_get_make(void) {
    return chaz_Make.make_command;
}

int
chaz_Make_shell_type(void) {
    return chaz_Make.shell_type;
}

static int
S_chaz_Make_detect(const char *make1, ...) {
    va_list args;
    const char *candidate;
    int found = 0;
    const char makefile_content[] =
        "foo:\n"
        "\t@echo 643490c943525d19\n"
        "\n"
        "%.ext:\n"
        "\t@echo 8f4ef20576b070d5\n";
    chaz_Util_write_file("_charm_Makefile", makefile_content);

    /* Audition candidates. */
    found = S_chaz_Make_audition(make1);
    va_start(args, make1);
    while (!found && (NULL != (candidate = va_arg(args, const char*)))) {
        found = S_chaz_Make_audition(candidate);
    }
    va_end(args);

    chaz_Util_remove_and_verify("_charm_Makefile");

    return found;
}

static int
S_chaz_Make_audition(const char *make) {
    int succeeded = 0;
    char *command = chaz_Util_join(" ", make, "-f", "_charm_Makefile", NULL);

    chaz_Util_remove_and_verify("_charm_foo");
    chaz_OS_run_redirected(command, "_charm_foo");
    if (chaz_Util_can_open_file("_charm_foo")) {
        size_t len;
        char *content = chaz_Util_slurp_file("_charm_foo", &len);
        if (content != NULL && strstr(content, "643490c943525d19") != NULL) {
            succeeded = 1;
        }
        free(content);
    }
    chaz_Util_remove_and_verify("_charm_foo");
    free(command);

    if (succeeded) {
        chaz_Make.make_command = chaz_Util_strdup(make);

        command = chaz_Util_join(" ", make, "-f", "_charm_Makefile", "foo.ext",
                                 NULL);
        chaz_OS_run_redirected(command, "_charm_foo");
        if (chaz_Util_can_open_file("_charm_foo")) {
            size_t len;
            char *content = chaz_Util_slurp_file("_charm_foo", &len);
            if (content != NULL
                && strstr(content, "8f4ef20576b070d5") != NULL
               ) {
                chaz_Make.supports_pattern_rules = 1;
            }
            free(content);
        }
        chaz_Util_remove_and_verify("_charm_foo");
        free(command);
    }

    return succeeded;
}

chaz_MakeFile*
chaz_MakeFile_new() {
    chaz_MakeFile *self = (chaz_MakeFile*)calloc(1, sizeof(chaz_MakeFile));
    const char    *exe_ext  = chaz_CC_exe_ext();
    const char    *obj_ext  = chaz_CC_obj_ext();
    char *generated;

    self->vars     = (chaz_MakeVar**)calloc(1, sizeof(chaz_MakeVar*));
    self->rules    = (chaz_MakeRule**)calloc(1, sizeof(chaz_MakeRule*));
    self->binaries = (chaz_MakeBinary**)calloc(1, sizeof(chaz_MakeBinary*));

    self->clean     = S_chaz_MakeRule_new("clean", NULL);
    self->distclean = S_chaz_MakeRule_new("distclean", "clean");

    generated = chaz_Util_join("", "charmonizer", exe_ext, " charmonizer",
                               obj_ext, " charmony.h Makefile", NULL);
    chaz_MakeRule_add_rm_command(self->distclean, generated);

    free(generated);
    return self;
}

void
chaz_MakeFile_destroy(chaz_MakeFile *self) {
    size_t i;

    for (i = 0; self->vars[i]; i++) {
        chaz_MakeVar *var = self->vars[i];
        free(var->name);
        free(var->value);
        free(var);
    }
    free(self->vars);

    for (i = 0; self->rules[i]; i++) {
        S_chaz_MakeRule_destroy(self->rules[i]);
    }
    free(self->rules);

    for (i = 0; self->binaries[i]; i++) {
        S_chaz_MakeBinary_destroy(self->binaries[i]);
    }
    free(self->binaries);

    S_chaz_MakeRule_destroy(self->clean);
    S_chaz_MakeRule_destroy(self->distclean);

    free(self);
}

chaz_MakeVar*
chaz_MakeFile_add_var(chaz_MakeFile *self, const char *name,
                      const char *value) {
    chaz_MakeVar  *var      = (chaz_MakeVar*)malloc(sizeof(chaz_MakeVar));
    chaz_MakeVar **vars     = self->vars;
    size_t         num_vars = self->num_vars + 1;

    var->name         = chaz_Util_strdup(name);
    var->value        = chaz_Util_strdup("");
    var->num_elements = 0;

    if (value) { chaz_MakeVar_append(var, value); }

    vars = (chaz_MakeVar**)realloc(vars,
                                   (num_vars + 1) * sizeof(chaz_MakeVar*));
    vars[num_vars-1] = var;
    vars[num_vars]   = NULL;
    self->vars = vars;
    self->num_vars = num_vars;

    return var;
}

chaz_MakeRule*
chaz_MakeFile_add_rule(chaz_MakeFile *self, const char *target,
                       const char *prereq) {
    chaz_MakeRule  *rule      = S_chaz_MakeRule_new(target, prereq);
    chaz_MakeRule **rules     = self->rules;
    size_t          num_rules = self->num_rules + 1;

    rules = (chaz_MakeRule**)realloc(rules,
                                     (num_rules + 1) * sizeof(chaz_MakeRule*));
    rules[num_rules-1] = rule;
    rules[num_rules]   = NULL;
    self->rules = rules;
    self->num_rules = num_rules;

    return rule;
}

chaz_MakeRule*
chaz_MakeFile_clean_rule(chaz_MakeFile *self) {
    return self->clean;
}

chaz_MakeRule*
chaz_MakeFile_distclean_rule(chaz_MakeFile *self) {
    return self->distclean;
}

chaz_MakeBinary*
chaz_MakeFile_add_exe(chaz_MakeFile *self, const char *dir,
                      const char *basename) {
    const char *exe_ext = chaz_CC_exe_ext();
    char *target;
    chaz_MakeBinary *binary;

    if (dir == NULL || strcmp(dir, ".") == 0) {
        target = chaz_Util_join("", basename, exe_ext, NULL);
    }
    else {
        const char *dir_sep = chaz_OS_dir_sep();
        target = chaz_Util_join("", dir, dir_sep, basename, exe_ext, NULL);
    }

    binary = S_chaz_MakeFile_add_binary(self, CHAZ_MAKEBINARY_EXE, dir,
                                        basename, target);

    free(target);
    return binary;
}

void
S_chaz_MakeFile_finish_exe(chaz_MakeFile *self, chaz_MakeBinary *binary) {
    const char *link = chaz_CC_link_command();
    const char *link_flags_string;
    char *command;

    (void)self;

    /* This is destructive but shouldn't be a problem since a Makefile
     * is only written once.
     */
    chaz_CFlags_set_link_output(binary->link_flags, "$@");
    link_flags_string = chaz_CFlags_get_string(binary->link_flags);

    /* Objects in dollar var must come before flags since flags may
     * contain libraries.
     */
    command = chaz_Util_join(" ", link, binary->dollar_var, link_flags_string,
                             NULL);
    chaz_MakeRule_add_command(binary->rule, command);
    free(command);
}

chaz_MakeBinary*
chaz_MakeFile_add_shared_lib(chaz_MakeFile *self, const char *dir,
                             const char *basename, const char *version,
                             const char *major_version) {
    int binary_format = chaz_CC_binary_format();
    char *target;
    chaz_MakeBinary *binary;

    if (binary_format == CHAZ_CC_BINFMT_PE) {
        target = chaz_CC_shared_lib_filename(dir, basename, major_version);
    }
    else {
        target = chaz_CC_shared_lib_filename(dir, basename, version);
    }

    binary = S_chaz_MakeFile_add_binary(self, CHAZ_MAKEBINARY_SHARED_LIB, dir,
                                        basename, target);
    binary->version       = chaz_Util_strdup(version);
    binary->major_version = chaz_Util_strdup(major_version);

    chaz_CFlags_compile_shared_library(binary->compile_flags);
    chaz_CFlags_link_shared_library(binary->link_flags, basename, version,
                                    major_version);

    free(target);
    return binary;
}

void
S_chaz_MakeFile_finish_shared_lib(chaz_MakeFile *self,
                                  chaz_MakeBinary *binary) {
    const char *link = chaz_CC_link_command();
    const char *link_flags_string;
    int binfmt = chaz_CC_binary_format();
    char *no_v_name
        = chaz_CC_shared_lib_filename(binary->target_dir, binary->basename,
                                      NULL);
    char *major_v_name
        = chaz_CC_shared_lib_filename(binary->target_dir, binary->basename,
                                      binary->major_version);
    char *command;

    if (binfmt == CHAZ_CC_BINFMT_MACHO) {
        const char *dir_sep = chaz_OS_dir_sep();
        char *install_name;

        /* Set temporary install name with full path on Darwin. */
        install_name = chaz_Util_join("", "-install_name $(CURDIR)", dir_sep,
                                      major_v_name, NULL);
        chaz_CFlags_append(binary->link_flags, install_name);
        free(install_name);
    }

    chaz_CFlags_set_link_output(binary->link_flags, "$@");
    link_flags_string = chaz_CFlags_get_string(binary->link_flags);

    command = chaz_Util_join(" ", link, binary->dollar_var, link_flags_string,
                             NULL);
    chaz_MakeRule_add_command(binary->rule, command);
    free(command);

    /* Add symlinks. */
    if (binfmt == CHAZ_CC_BINFMT_ELF || binfmt == CHAZ_CC_BINFMT_MACHO) {
        command = chaz_Util_join(" ", "ln -sf", binary->rule->targets,
                                 major_v_name, NULL);
        chaz_MakeRule_add_command(binary->rule, command);
        free(command);

        if (binfmt == CHAZ_CC_BINFMT_MACHO) {
            command = chaz_Util_join(" ", "ln -sf", binary->rule->targets,
                                     no_v_name, NULL);
        }
        else {
            command = chaz_Util_join(" ", "ln -sf", major_v_name, no_v_name,
                                     NULL);
        }
        chaz_MakeRule_add_command(binary->rule, command);
        free(command);

        chaz_MakeRule_add_rm_command(self->clean, major_v_name);
        chaz_MakeRule_add_rm_command(self->clean, no_v_name);
    }

    if (binfmt == CHAZ_CC_BINFMT_PE) {
        /* Remove import library. */
        char *filename
            = chaz_CC_import_lib_filename(binary->target_dir, binary->basename,
                                          binary->major_version);
        chaz_MakeRule_add_rm_command(self->clean, filename);
        free(filename);
    }

    if (chaz_CC_msvc_version_num()) {
        /* Remove export file. */
        char *filename
            = chaz_CC_export_filename(binary->target_dir, binary->basename,
                                      binary->major_version);
        chaz_MakeRule_add_rm_command(self->clean, filename);
        free(filename);
    }

    free(major_v_name);
    free(no_v_name);
}

chaz_MakeBinary*
chaz_MakeFile_add_static_lib(chaz_MakeFile *self, const char *dir,
                             const char *basename) {
    char *target = chaz_CC_static_lib_filename(dir, basename);
    chaz_MakeBinary *binary
        = S_chaz_MakeFile_add_binary(self, CHAZ_MAKEBINARY_STATIC_LIB, dir,
                                     basename, target);

    free(target);
    return binary;
}

static void
S_chaz_MakeFile_finish_static_lib(chaz_MakeFile *self,
                                  chaz_MakeBinary *binary) {
    char *command;

    (void)self;

    command = chaz_CC_format_archiver_command("$@", binary->dollar_var);
    chaz_MakeRule_add_command(binary->rule, command);
    free(command);

    command = chaz_CC_format_ranlib_command("$@");
    if (command) {
        chaz_MakeRule_add_command(binary->rule, command);
        free(command);
    }
}

static chaz_MakeBinary*
S_chaz_MakeFile_add_binary(chaz_MakeFile *self, int type, const char *dir,
                           const char *basename, const char *target) {
    chaz_MakeBinary *binary
        = (chaz_MakeBinary*)calloc(1, sizeof(chaz_MakeBinary));
    const char *suffix;
    char *uc_basename = chaz_Util_strdup(basename);
    char *binary_var_name;
    char *obj_var_name;
    char *dollar_var;
    size_t i;
    size_t num_binaries;
    size_t alloc_size;
    chaz_MakeBinary **binaries;

    switch (type) {
        case CHAZ_MAKEBINARY_EXE:        suffix = "EXE";        break;
        case CHAZ_MAKEBINARY_STATIC_LIB: suffix = "STATIC_LIB"; break;
        case CHAZ_MAKEBINARY_SHARED_LIB: suffix = "SHARED_LIB"; break;
        default:
            chaz_Util_die("Unknown binary type %d", type);
            return NULL;
    }

    for (i = 0; uc_basename[i] != '\0'; i++) {
        uc_basename[i] = toupper((unsigned char)uc_basename[i]);
    }

    binary_var_name = chaz_Util_join("_", uc_basename, suffix, NULL);
    obj_var_name    = chaz_Util_join("_", uc_basename, suffix, "OBJS", NULL);
    dollar_var      = chaz_Util_join("", "$(", obj_var_name, ")", NULL);

    chaz_MakeFile_add_var(self, binary_var_name, target);

    binary->type           = type;
    binary->target_dir     = dir ? chaz_Util_strdup(dir) : NULL;
    binary->basename       = chaz_Util_strdup(basename);
    binary->obj_var        = chaz_MakeFile_add_var(self, obj_var_name, NULL);
    binary->dollar_var     = dollar_var;
    binary->rule           = S_chaz_MakeRule_new(target, dollar_var);
    binary->sources        = (char**)calloc(1, sizeof(char*));
    binary->single_sources = (char**)calloc(1, sizeof(char*));
    binary->dirs           = (char**)calloc(1, sizeof(char*));
    binary->compile_flags  = chaz_CC_new_cflags();
    binary->link_flags     = chaz_CC_new_cflags();

    num_binaries = self->num_binaries;
    alloc_size   = (num_binaries + 2) * sizeof(chaz_MakeBinary*);
    binaries     = (chaz_MakeBinary**)realloc(self->binaries, alloc_size);
    binaries[num_binaries]   = binary;
    binaries[num_binaries+1] = NULL;
    self->binaries     = binaries;
    self->num_binaries = num_binaries + 1;

    free(uc_basename);
    free(obj_var_name);
    free(binary_var_name);
    return binary;
}

chaz_MakeBinary*
chaz_MakeFile_add_lemon_exe(chaz_MakeFile *self, const char *dir) {
    chaz_MakeBinary *exe = chaz_MakeFile_add_exe(self, dir, "lemon");
    chaz_MakeBinary_add_src_file(exe, dir, "lemon.c");
    return exe;
}

chaz_MakeRule*
chaz_MakeFile_add_lemon_grammar(chaz_MakeFile *self,
                                const char *base_name) {
    char *c_file  = chaz_Util_join(".", base_name, "c", NULL);
    char *h_file  = chaz_Util_join(".", base_name, "h", NULL);
    char *y_file  = chaz_Util_join(".", base_name, "y", NULL);
    char *command = chaz_Util_join(" ", "$(LEMON_EXE) -q", y_file, NULL);

    chaz_MakeRule *rule = chaz_MakeFile_add_rule(self, c_file, y_file);
    chaz_MakeRule *clean_rule = chaz_MakeFile_clean_rule(self);

    chaz_MakeRule_add_prereq(rule, "$(LEMON_EXE)");
    chaz_MakeRule_add_command(rule, command);

    chaz_MakeRule_add_rm_command(clean_rule, h_file);
    chaz_MakeRule_add_rm_command(clean_rule, c_file);

    free(c_file);
    free(h_file);
    free(y_file);
    free(command);
    return rule;
}

void
chaz_MakeFile_write(chaz_MakeFile *self) {
    FILE   *out;
    size_t  i;

    out = fopen("Makefile", "w");
    if (!out) {
        chaz_Util_die("Can't open Makefile\n");
    }

    if (chaz_Make.shell_type == CHAZ_OS_CMD_EXE) {
        /* Make sure that mingw32-make uses the cmd.exe shell. */
        fprintf(out, "SHELL = cmd\n");
    }

    for (i = 0; self->vars[i]; i++) {
        chaz_MakeVar *var = self->vars[i];
        fprintf(out, "%s = %s\n", var->name, var->value);
    }
    fprintf(out, "\n");

    for (i = 0; self->rules[i]; i++) {
        S_chaz_MakeRule_write(self->rules[i], out);
    }

    for (i = 0; self->binaries[i]; i++) {
        S_chaz_MakeFile_write_binary_rules(self, self->binaries[i], out);
    }

    S_chaz_MakeRule_write(self->clean, out);
    S_chaz_MakeRule_write(self->distclean, out);

    /* Suffix rule for .c files. */
    if (chaz_CC_msvc_version_num()) {
        fprintf(out, ".c.obj :\n");
        fprintf(out, "\t$(CC) /nologo $(CFLAGS) /c $< /Fo$@\n\n");
    }
    else {
        fprintf(out, ".c.o :\n");
        fprintf(out, "\t$(CC) $(CFLAGS) -c $< -o $@\n\n");
    }

    fclose(out);
}

static void
S_chaz_MakeFile_write_binary_rules(chaz_MakeFile *self,
                                   chaz_MakeBinary *binary, FILE *out) {
    const char *cflags;

    if (chaz_CC_msvc_version_num()) {
        chaz_CFlags_append(binary->compile_flags, "/nologo");
        chaz_CFlags_append(binary->link_flags, "/nologo");
    }

    switch (binary->type) {
        case CHAZ_MAKEBINARY_EXE:
            S_chaz_MakeFile_finish_exe(self, binary);
            break;
        case CHAZ_MAKEBINARY_STATIC_LIB:
            S_chaz_MakeFile_finish_static_lib(self, binary);
            break;
        case CHAZ_MAKEBINARY_SHARED_LIB:
            S_chaz_MakeFile_finish_shared_lib(self, binary);
            break;
        default:
            chaz_Util_die("Invalid binary type: %d", binary->type);
            return;
    }

    chaz_MakeRule_add_rm_command(self->clean, binary->rule->targets);
    chaz_MakeRule_add_rm_command(self->clean, binary->dollar_var);

    S_chaz_MakeRule_write(binary->rule, out);

    cflags = chaz_CFlags_get_string(binary->compile_flags);

    /* Write rules to compile with custom flags. */
    if (cflags[0] != '\0') {
        if (!chaz_Make.supports_pattern_rules
            || chaz_Make.shell_type == CHAZ_OS_CMD_EXE) {
            /* Write a rule for each object file. This is needed for make
             * utilities that don't support pattern rules but also for
             * mingw32-make which has problems with pattern rules and
             * backslash directory separators.
             */
            S_chaz_MakeFile_write_object_rules(binary->sources, cflags, out);
        }
        else {
            /* Write a pattern rule for each directory. */
            S_chaz_MakeFile_write_pattern_rules(binary->dirs, cflags, out);
            /* Write a rule for each object added with add_src_file. */
            S_chaz_MakeFile_write_object_rules(binary->single_sources, cflags,
                                               out);
        }
    }
}

static void
S_chaz_MakeFile_write_object_rules(char **sources, const char *cflags,
                                   FILE *out) {
    chaz_CFlags *output_cflags = chaz_CC_new_cflags();
    const char *output_cflags_string;
    size_t i;

    chaz_CFlags_set_output_obj(output_cflags, "$@");
    output_cflags_string = chaz_CFlags_get_string(output_cflags);

    for (i = 0; sources[i]; i++) {
        const char *source = sources[i];
        char *obj_path = S_chaz_MakeBinary_obj_path(source);
        chaz_MakeRule *rule;
        char *command;

        if (obj_path == NULL) { continue; }

        rule = S_chaz_MakeRule_new(obj_path, source);
        command = chaz_Util_join(" ", "$(CC) $(CFLAGS)", cflags, source,
                                 output_cflags_string, NULL);
        chaz_MakeRule_add_command(rule, command);
        S_chaz_MakeRule_write(rule, out);

        free(command);
        S_chaz_MakeRule_destroy(rule);
        free(obj_path);
    }

    chaz_CFlags_destroy(output_cflags);
}

static void
S_chaz_MakeFile_write_pattern_rules(char **dirs, const char *cflags,
                                    FILE *out) {
    const char *obj_ext = chaz_CC_obj_ext();
    const char *dir_sep = chaz_OS_dir_sep();
    chaz_CFlags *output_cflags = chaz_CC_new_cflags();
    const char *output_cflags_string;
    char *command;
    size_t i;

    chaz_CFlags_set_output_obj(output_cflags, "$@");
    output_cflags_string = chaz_CFlags_get_string(output_cflags);
    command  = chaz_Util_join(" ", "$(CC) $(CFLAGS)", cflags, "$<",
                              output_cflags_string, NULL);

    for (i = 0; dirs[i]; i++) {
        const char *dir = dirs[i];
        char *target = chaz_Util_join("", dir, dir_sep, "%", obj_ext,
                                      NULL);
        char *prereq = chaz_Util_join("", dir, dir_sep, "%.c", NULL);
        chaz_MakeRule *rule = S_chaz_MakeRule_new(target, prereq);

        chaz_MakeRule_add_command(rule, command);
        S_chaz_MakeRule_write(rule, out);

        S_chaz_MakeRule_destroy(rule);
        free(prereq);
        free(target);
    }

    free(command);
    chaz_CFlags_destroy(output_cflags);
}

void
chaz_MakeVar_append(chaz_MakeVar *self, const char *element) {
    char *value;

    if (element[0] == '\0') { return; }

    if (self->num_elements == 0) {
        value = chaz_Util_strdup(element);
    }
    else {
        value = (char*)malloc(strlen(self->value) + strlen(element) + 20);

        if (self->num_elements == 1) {
            sprintf(value, "\\\n    %s \\\n    %s", self->value, element);
        }
        else {
            sprintf(value, "%s \\\n    %s", self->value, element);
        }
    }

    free(self->value);
    self->value = value;
    self->num_elements++;
}

static chaz_MakeRule*
S_chaz_MakeRule_new(const char *target, const char *prereq) {
    chaz_MakeRule *rule = (chaz_MakeRule*)malloc(sizeof(chaz_MakeRule));

    rule->targets  = NULL;
    rule->prereqs  = NULL;
    rule->commands = NULL;

    if (target) { chaz_MakeRule_add_target(rule, target); }
    if (prereq) { chaz_MakeRule_add_prereq(rule, prereq); }

    return rule;
}

static void
S_chaz_MakeRule_destroy(chaz_MakeRule *self) {
    if (self->targets)  { free(self->targets); }
    if (self->prereqs)  { free(self->prereqs); }
    if (self->commands) { free(self->commands); }
    free(self);
}

static void
S_chaz_MakeRule_write(chaz_MakeRule *self, FILE *out) {
    fprintf(out, "%s :", self->targets);
    if (self->prereqs) {
        fprintf(out, " %s", self->prereqs);
    }
    fprintf(out, "\n");
    if (self->commands) {
        fprintf(out, "%s", self->commands);
    }
    fprintf(out, "\n");
}

void
chaz_MakeRule_add_target(chaz_MakeRule *self, const char *target) {
    char *targets;

    if (!self->targets) {
        targets = chaz_Util_strdup(target);
    }
    else {
        targets = chaz_Util_join(" ", self->targets, target, NULL);
        free(self->targets);
    }

    self->targets = targets;
}

void
chaz_MakeRule_add_prereq(chaz_MakeRule *self, const char *prereq) {
    char *prereqs;

    if (!self->prereqs) {
        prereqs = chaz_Util_strdup(prereq);
    }
    else {
        prereqs = chaz_Util_join(" ", self->prereqs, prereq, NULL);
        free(self->prereqs);
    }

    self->prereqs = prereqs;
}

void
chaz_MakeRule_add_command(chaz_MakeRule *self, const char *command) {
    char *commands;

    if (!self->commands) {
        commands = (char*)malloc(strlen(command) + 20);
        sprintf(commands, "\t%s\n", command);
    }
    else {
        commands = (char*)malloc(strlen(self->commands) + strlen(command) + 20);
        sprintf(commands, "%s\t%s\n", self->commands, command);
        free(self->commands);
    }

    self->commands = commands;
}

void
chaz_MakeRule_add_rm_command(chaz_MakeRule *self, const char *files) {
    char *command;

    if (chaz_Make.shell_type == CHAZ_OS_POSIX) {
        command = chaz_Util_join(" ", "rm -f", files, NULL);
    }
    else if (chaz_Make.shell_type == CHAZ_OS_CMD_EXE) {
        command = chaz_Util_join("", "for %%i in (", files,
                                 ") do @if exist %%i del /f %%i", NULL);
    }
    else {
        chaz_Util_die("Unsupported shell type: %d", chaz_Make.shell_type);
    }

    chaz_MakeRule_add_command(self, command);
    free(command);
}

void
chaz_MakeRule_add_recursive_rm_command(chaz_MakeRule *self, const char *dirs) {
    char *command;

    if (chaz_Make.shell_type == CHAZ_OS_POSIX) {
        command = chaz_Util_join(" ", "rm -rf", dirs, NULL);
    }
    else if (chaz_Make.shell_type == CHAZ_OS_CMD_EXE) {
        command = chaz_Util_join("", "for %%i in (", dirs,
                                 ") do @if exist %%i rmdir /s /q %%i", NULL);
    }
    else {
        chaz_Util_die("Unsupported shell type: %d", chaz_Make.shell_type);
    }

    chaz_MakeRule_add_command(self, command);
    free(command);
}

void
chaz_MakeRule_add_make_command(chaz_MakeRule *self, const char *dir,
                               const char *target) {
    char *command;

    if (chaz_Make.shell_type == CHAZ_OS_POSIX) {
        if (!target) {
            command = chaz_Util_join("", "(cd ", dir, " && $(MAKE))", NULL);
        }
        else {
            command = chaz_Util_join("", "(cd ", dir, " && $(MAKE) ", target,
                                     ")", NULL);
        }
        chaz_MakeRule_add_command(self, command);
        free(command);
    }
    else if (chaz_Make.shell_type == CHAZ_OS_CMD_EXE) {
        if (!target) {
            command = chaz_Util_join(" ", "pushd", dir, "&& $(MAKE) && popd",
                                     NULL);
        }
        else {
            command = chaz_Util_join(" ", "pushd", dir, "&& $(MAKE)", target,
                                     "&& popd", NULL);
        }
        chaz_MakeRule_add_command(self, command);
        free(command);
    }
    else {
        chaz_Util_die("Unsupported shell type: %d", chaz_Make.shell_type);
    }
}

static void
S_chaz_MakeBinary_destroy(chaz_MakeBinary *self) {
    size_t i;

    free(self->target_dir);
    free(self->basename);
    free(self->version);
    free(self->major_version);
    free(self->dollar_var);
    S_chaz_MakeRule_destroy(self->rule);

    for (i = 0; i < self->num_sources; i++) {
        free(self->sources[i]);
    }
    free(self->sources);
    for (i = 0; i < self->num_single_sources; i++) {
        free(self->single_sources[i]);
    }
    free(self->single_sources);
    for (i = 0; i < self->num_dirs; i++) {
        free(self->dirs[i]);
    }
    free(self->dirs);

    chaz_CFlags_destroy(self->compile_flags);
    chaz_CFlags_destroy(self->link_flags);

    free(self);
}

void
chaz_MakeBinary_add_src_file(chaz_MakeBinary *self, const char *dir,
                             const char *filename) {
    size_t num_sources = self->num_single_sources;
    size_t alloc_size  = (num_sources + 2) * sizeof(char*);
    char **sources = (char**)realloc(self->single_sources, alloc_size);
    char *path;

    if (dir == NULL || strcmp(dir, ".") == 0) {
        path = chaz_Util_strdup(filename);
    }
    else {
        const char *dir_sep = chaz_OS_dir_sep();
        path = chaz_Util_join(dir_sep, dir, filename, NULL);
    }

    /* Add to single_sources. */
    sources[num_sources]     = path;
    sources[num_sources+1]   = NULL;
    self->single_sources     = sources;
    self->num_single_sources = num_sources + 1;

    S_chaz_MakeBinary_do_add_src_file(self, path);
}

void
chaz_MakeBinary_add_src_dir(chaz_MakeBinary *self, const char *path) {
    chaz_MakeBinary_add_filtered_src_dir(self, path, NULL, NULL);
}

void
chaz_MakeBinary_add_filtered_src_dir(chaz_MakeBinary *self, const char *path,
                                     chaz_Make_file_filter_t filter,
                                     void *filter_ctx) {
    chaz_MakeBinaryContext context;
    size_t num_dirs = self->num_dirs;
    char **dirs = (char**)realloc(self->dirs, (num_dirs + 2) * sizeof(char*));

    dirs[num_dirs]   = chaz_Util_strdup(path);
    dirs[num_dirs+1] = NULL;
    self->dirs     = dirs;
    self->num_dirs = num_dirs + 1;

    context.binary     = self;
    context.filter     = filter;
    context.filter_ctx = filter_ctx;

    chaz_Make_list_files(path, "c", S_chaz_MakeBinary_list_files_callback,
                         &context);
}

static void
S_chaz_MakeBinary_list_files_callback(const char *dir, char *file,
                                      void *vcontext) {
    chaz_MakeBinaryContext *context = (chaz_MakeBinaryContext*)vcontext;
    const char *dir_sep = chaz_OS_dir_sep();

    if (context->filter == NULL
        || context->filter(dir, file, context->filter_ctx) != 0
       ) {
        char *path = chaz_Util_join(dir_sep, dir, file, NULL);
        S_chaz_MakeBinary_do_add_src_file(context->binary, path);
        free(path);
    }
}

static void
S_chaz_MakeBinary_do_add_src_file(chaz_MakeBinary *self, const char *path) {
    size_t num_sources = self->num_sources;
    size_t alloc_size  = (num_sources + 2) * sizeof(char*);
    char **sources = (char**)realloc(self->sources, alloc_size);
    char *obj_path;

    sources[num_sources]   = chaz_Util_strdup(path);
    sources[num_sources+1] = NULL;
    self->sources     = sources;
    self->num_sources = num_sources + 1;

    obj_path = S_chaz_MakeBinary_obj_path(path);
    if (obj_path == NULL) {
        chaz_Util_warn("Invalid source filename: %s", path);
    }
    else {
        chaz_MakeVar_append(self->obj_var, obj_path);
        free(obj_path);
    }
}

static char*
S_chaz_MakeBinary_obj_path(const char *src_path) {
    const char *dir_sep = chaz_OS_dir_sep();
    const char *obj_ext = chaz_CC_obj_ext();
    size_t obj_ext_len = strlen(obj_ext);
    size_t i = strlen(src_path);
    char *retval;

    while (i > 0) {
        i -= 1;
        if (src_path[i] == dir_sep[0]) { return NULL; }
        if (src_path[i] == '.')        { break; }
    }

    if (src_path[i] != '.') { return NULL; }

    retval = (char*)malloc(i + obj_ext_len + 1);
    memcpy(retval, src_path, i);
    memcpy(retval + i, obj_ext, obj_ext_len + 1);

    return retval;
}

void
chaz_MakeBinary_add_prereq(chaz_MakeBinary *self, const char *prereq) {
    chaz_MakeRule_add_prereq(self->rule, prereq);
}

char*
chaz_MakeBinary_obj_string(chaz_MakeBinary *self) {
    char *retval = chaz_Util_strdup("");
    size_t i;

    for (i = 0; i < self->num_sources; i++) {
        const char *sep = retval[0] == '\0' ? "" : " ";
        char *obj_path = S_chaz_MakeBinary_obj_path(self->sources[i]);
        char *tmp;

        if (obj_path == NULL) { continue; }

        tmp = chaz_Util_join("", retval, sep, obj_path, NULL);
        free(retval);
        retval = tmp;

        free(obj_path);
    }

    return retval;
}

const char*
chaz_MakeBinary_get_target(chaz_MakeBinary *self) {
    return self->rule->targets;
}

chaz_CFlags*
chaz_MakeBinary_get_compile_flags(chaz_MakeBinary *self) {
    return self->compile_flags;
}

chaz_CFlags*
chaz_MakeBinary_get_link_flags(chaz_MakeBinary *self) {
    return self->link_flags;
}

void
chaz_Make_list_files(const char *dir, const char *ext,
                     chaz_Make_file_callback_t callback, void *context) {
    int         shell_type = chaz_OS_shell_type();
    const char *pattern;
    char       *command;
    char       *list;
    char       *prefix;
    char       *file;
    size_t      command_size;
    size_t      list_len;
    size_t      prefix_len;

    /* List files using shell. */

    if (shell_type == CHAZ_OS_POSIX) {
        pattern = "find %s -name '*.%s' -type f";
    }
    else if (shell_type == CHAZ_OS_CMD_EXE) {
        pattern = "dir %s\\*.%s /s /b /a-d";
    }
    else {
        chaz_Util_die("Unknown shell type %d", shell_type);
    }

    command_size = strlen(pattern) + strlen(dir) + strlen(ext) + 10;
    command = (char*)malloc(command_size);
    sprintf(command, pattern, dir, ext);
    list = chaz_OS_run_and_capture(command, &list_len);
    free(command);
    if (!list) {
        chaz_Util_die("Failed to list files in '%s'", dir);
    }
    list[list_len-1] = 0;

    /* Find directory prefix to strip from files */

    if (shell_type == CHAZ_OS_POSIX) {
        prefix_len = strlen(dir);
        prefix = (char*)malloc(prefix_len + 2);
        memcpy(prefix, dir, prefix_len);
        prefix[prefix_len++] = '/';
        prefix[prefix_len]   = '\0';
    }
    else {
        char   *output;
        size_t  output_len;

        /* 'dir /s' returns absolute paths, so we have to find the absolute
         * path of the directory. This is done by using the variable
         * substitution feature of the 'for' command.
         */
        pattern = "for %%I in (%s) do @echo %%~fI";
        command_size = strlen(pattern) + strlen(dir) + 10;
        command = (char*)malloc(command_size);
        sprintf(command, pattern, dir);
        output = chaz_OS_run_and_capture(command, &output_len);
        free(command);
        if (!output) { chaz_Util_die("Failed to find absolute path"); }

        /* Strip whitespace from end of output. */
        for (prefix_len = output_len; prefix_len > 0; --prefix_len) {
            if (!isspace((unsigned char)output[prefix_len-1])) { break; }
        }
        prefix = (char*)malloc(prefix_len + 2);
        memcpy(prefix, output, prefix_len);
        prefix[prefix_len++] = '\\';
        prefix[prefix_len]   = '\0';
        free(output);
    }

    /* Iterate file list and invoke callback. */

    for (file = strtok(list, "\r\n"); file; file = strtok(NULL, "\r\n")) {
        if (strlen(file) <= prefix_len
            || memcmp(file, prefix, prefix_len) != 0
           ) {
            chaz_Util_die("Expected prefix '%s' for file name '%s'", prefix,
                          file);
        }

        callback(dir, file + prefix_len, context);
    }

    free(prefix);
    free(list);
}


/***************************************************************************/

#line 17 "src/Charmonizer/Core/OperatingSystem.c"
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <ctype.h>
#include <time.h>
#include <errno.h>

/* #include "Charmonizer/Core/Compiler.h" */
/* #include "Charmonizer/Core/Util.h" */
/* #include "Charmonizer/Core/ConfWriter.h" */
/* #include "Charmonizer/Core/OperatingSystem.h" */

#define CHAZ_OS_TARGET_PATH  "_charmonizer_target"
#define CHAZ_OS_NAME_MAX     31

static struct {
    char name[CHAZ_OS_NAME_MAX+1];
    char dev_null[20];
    char dir_sep[2];
    char local_command_start[3];
    int  shell_type;
    int  run_sh_via_cmd_exe;
} chaz_OS = { "", "", "", "", 0, 0 };

static int
chaz_OS_run_sh_via_cmd_exe(const char *command, const char *path);

void
chaz_OS_init(void) {
    char *output;
    size_t output_len;

    if (chaz_Util_verbosity) {
        printf("Initializing Charmonizer/Core/OperatingSystem...\n");
    }

    /* Detect shell based on escape character. */

    /* Needed to make redirection work. */
    chaz_OS.shell_type = CHAZ_OS_POSIX;

    output = chaz_OS_run_and_capture("echo foo\\^bar", &output_len);

    if (output_len >= 7 && memcmp(output, "foo\\bar", 7) == 0) {
        /* Escape character is caret. */
        if (chaz_Util_verbosity) {
            printf("Detected cmd.exe shell\n");
        }

        /* Try to see whether running commands via the `sh` command works.
         * Run the `find` command to check whether we're in a somewhat POSIX
         * compatible environment. */
        free(output);
        chaz_OS.run_sh_via_cmd_exe = 1;
        output = chaz_OS_run_and_capture("find . -prune", &output_len);

        if (output_len >= 2
            && output[0] == '.'
            && isspace((unsigned char)output[1])
           ) {
            if (chaz_Util_verbosity) {
                printf("Detected POSIX shell via cmd.exe\n");
            }
            chaz_OS.shell_type = CHAZ_OS_POSIX;
        }
        else {
            chaz_OS.shell_type = CHAZ_OS_CMD_EXE;
            chaz_OS.run_sh_via_cmd_exe = 0;
        }

        /* Redirection is always run through cmd.exe. */
        strcpy(chaz_OS.dev_null, "nul");
    }
    else if (output_len >= 7 && memcmp(output, "foo^bar", 7) == 0) {
        /* Escape character is backslash. */
        if (chaz_Util_verbosity) {
            printf("Detected POSIX shell\n");
        }
        chaz_OS.shell_type = CHAZ_OS_POSIX;
        strcpy(chaz_OS.dev_null, "/dev/null");
    }

    if (chaz_OS.shell_type == CHAZ_OS_CMD_EXE) {
        strcpy(chaz_OS.dir_sep, "\\");
        /* Empty string should work, too. */
        strcpy(chaz_OS.local_command_start, ".\\");
    }
    else if (chaz_OS.shell_type == CHAZ_OS_POSIX) {
        strcpy(chaz_OS.dir_sep, "/");
        strcpy(chaz_OS.local_command_start, "./");
    }
    else {
        chaz_Util_die("Couldn't identify shell");
    }

    free(output);
}

const char*
chaz_OS_dev_null(void) {
    return chaz_OS.dev_null;
}

const char*
chaz_OS_dir_sep(void) {
    return chaz_OS.dir_sep;
}

int
chaz_OS_shell_type(void) {
    return chaz_OS.shell_type;
}

int
chaz_OS_remove(const char *name) {
    /*
     * On Windows it can happen that another process, typically a
     * virus scanner, still has an open handle on the file. This can
     * make the subsequent recreation of a file with the same name
     * fail. As a workaround, files are renamed to a random name
     * before deletion.
     */
    int retval = 0;

    static const size_t num_random_chars = 16;

    size_t  name_len = strlen(name);
    size_t  i;
    char   *temp_name = (char*)malloc(name_len + num_random_chars + 1);
    const char *working_name = name;
    clock_t start, now;

    strcpy(temp_name, name);
    for (i = 0; i < num_random_chars; i++) {
        temp_name[name_len+i] = 'A' + rand() % 26;
    }
    temp_name[name_len+num_random_chars] = '\0';

    /* Try over and over again for around 1 second to rename the file.
     * Ideally we would sleep between attempts, but sleep functionality is not
     * portable. */
    start = now = clock();
    while (now - start < CLOCKS_PER_SEC) {
        now = clock();
        if (!rename(name, temp_name)) {
            /* The rename succeeded. */
            working_name = temp_name;
            break;
        }
        else if (errno == ENOENT) {
            /* No such file or directory, so no point in trying to remove it.
             * (Technically ENOENT is POSIX but hopefully this works.) */
            free(temp_name);
            return 0;
        }
    }

    /* Try over and over again for around 1 second to delete the file. */
    start = now = clock();
    while (!retval && now - start < CLOCKS_PER_SEC) {
        now = clock();
        retval = !remove(working_name);
    }

    free(temp_name);
    return retval;
}

int
chaz_OS_run_local_redirected(const char *command, const char *path) {
    char *local_command
        = chaz_Util_join("", chaz_OS.local_command_start, command, NULL);
    int retval = chaz_OS_run_redirected(local_command, path);
    free(local_command);
    return retval;
}

int
chaz_OS_run_quietly(const char *command) {
    return chaz_OS_run_redirected(command, chaz_OS.dev_null);
}

int
chaz_OS_run_redirected(const char *command, const char *path) {
    int retval = 1;
    char *quiet_command = NULL;
    if (chaz_OS.run_sh_via_cmd_exe) {
        return chaz_OS_run_sh_via_cmd_exe(command, path);
    }
    if (chaz_OS.shell_type == CHAZ_OS_POSIX
        || chaz_OS.shell_type == CHAZ_OS_CMD_EXE
        ) {
        quiet_command = chaz_Util_join(" ", command, ">", path, "2>&1", NULL);
    }
    else {
        chaz_Util_die("Don't know the shell type");
    }
    retval = system(quiet_command);
    free(quiet_command);
    return retval;
}

static int
chaz_OS_run_sh_via_cmd_exe(const char *command, const char *path) {
    size_t i;
    size_t size;
    char *escaped_command;
    char *wrapped_command;
    char *p;
    int retval;

    /* Compute size. */

    size = 0;

    for (i = 0; command[i] != '\0'; i++) {
        char c = command[i];

        switch (c) {
            case '"':
            case '\\':
                size += 2;
                break;

            case '%':
            case '!':
                size += 3;
                break;

            default:
                size += 1;
                break;
        }
    }

    /* Build sh command. */

    escaped_command = (char*)malloc(size + 1);
    p = escaped_command;

    /* Escape special characters. */

    for (i = 0; command[i] != '\0'; i++) {
        char c = command[i];

        switch (c) {
            case '"':
            case '\\':
                /* Escape double quote and backslash. */
                *p++ = '\\';
                *p++ = c;
                break;

            case '%':
            case '!':
                /* Break out of double quotes for percent sign and
                 * exclamation mark. This prevents variable expansion. */
                *p++ = '"';
                *p++ = c;
                *p++ = '"';
                break;

            default:
                *p++ = c;
                break;
        }
    }

    *p++ = '\0';

    /* Run sh command. */

    wrapped_command = chaz_Util_join("", "sh -c \"", escaped_command, "\" > ",
                                     path, " 2>&1", NULL);
    retval = system(wrapped_command);

    free(wrapped_command);
    free(escaped_command);
    return retval;
}

char*
chaz_OS_run_and_capture(const char *command, size_t *output_len) {
    char *output;
    chaz_OS_run_redirected(command, CHAZ_OS_TARGET_PATH);
    output = chaz_Util_slurp_file(CHAZ_OS_TARGET_PATH, output_len);
    chaz_Util_remove_and_verify(CHAZ_OS_TARGET_PATH);
    return output;
}

void
chaz_OS_mkdir(const char *filepath) {
    char *command = NULL;
    if (chaz_OS.shell_type == CHAZ_OS_POSIX
        || chaz_OS.shell_type == CHAZ_OS_CMD_EXE
       ) {
        command = chaz_Util_join(" ", "mkdir", filepath, NULL);
    }
    else {
        chaz_Util_die("Don't know the shell type");
    }
    chaz_OS_run_quietly(command);
    free(command);
}

void
chaz_OS_rmdir(const char *filepath) {
    char *command = NULL;
    if (chaz_OS.shell_type == CHAZ_OS_POSIX) {
        command = chaz_Util_join(" ", "rmdir", filepath, NULL);
    }
    else if (chaz_OS.shell_type == CHAZ_OS_CMD_EXE) {
        command = chaz_Util_join(" ", "rmdir", "/q", filepath, NULL);
    }
    else {
        chaz_Util_die("Don't know the shell type");
    }
    chaz_OS_run_quietly(command);
    free(command);
}


/***************************************************************************/

#line 17 "src/Charmonizer/Core/Util.c"
#include <errno.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
/* #include "Charmonizer/Core/Util.h" */
/* #include "Charmonizer/Core/OperatingSystem.h" */

/* va_copy is not part of C89. Assume that simple assignment works if it
 * isn't defined.
 */
#ifndef va_copy
  #define va_copy(dst, src) ((dst) = (src))
#endif

/* Global verbosity setting. */
int chaz_Util_verbosity = 1;

void
chaz_Util_write_file(const char *filename, const char *content) {
    FILE *fh = fopen(filename, "w+");
    size_t content_len = strlen(content);
    if (fh == NULL) {
        chaz_Util_die("Couldn't open '%s': %s", filename, strerror(errno));
    }
    fwrite(content, sizeof(char), content_len, fh);
    if (fclose(fh)) {
        chaz_Util_die("Error when closing '%s': %s", filename,
                      strerror(errno));
    }
}

char*
chaz_Util_slurp_file(const char *file_path, size_t *len_ptr) {
    FILE   *const file = fopen(file_path, "rb");
    char   *contents;
    size_t  len;
    long    check_val;

    /* Sanity check. */
    if (file == NULL) {
        chaz_Util_die("Error opening file '%s': %s", file_path,
                      strerror(errno));
    }

    /* Find length; return NULL if the file has a zero-length. */
    len = chaz_Util_flength(file);
    if (len == 0) {
        *len_ptr = 0;
        return NULL;
    }

    /* Allocate memory and read the file. */
    contents = (char*)malloc(len * sizeof(char) + 1);
    if (contents == NULL) {
        chaz_Util_die("Out of memory at %d, %s", __FILE__, __LINE__);
    }
    contents[len] = '\0';
    check_val = fread(contents, sizeof(char), len, file);

    /* Weak error check, because CRLF might result in fewer chars read. */
    if (check_val <= 0) {
        chaz_Util_die("Tried to read %d characters of '%s', got %d", (int)len,
                      file_path, check_val);
    }

    /* Set length pointer for benefit of caller. */
    *len_ptr = check_val;

    /* Clean up. */
    if (fclose(file)) {
        chaz_Util_die("Error closing file '%s': %s", file_path,
                      strerror(errno));
    }

    return contents;
}

long
chaz_Util_flength(void *file) {
    FILE *f = (FILE*)file;
    const long bookmark = ftell(f);
    long check_val;
    long len;

    /* Seek to end of file and check length. */
    check_val = fseek(f, 0, SEEK_END);
    if (check_val == -1) {
        chaz_Util_die("fseek error : %s\n", strerror(errno));
    }
    len = ftell(f);
    if (len == -1) { chaz_Util_die("ftell error : %s\n", strerror(errno)); }

    /* Return to where we were. */
    check_val = fseek(f, bookmark, SEEK_SET);
    if (check_val == -1) {
        chaz_Util_die("fseek error : %s\n", strerror(errno));
    }

    return len;
}

char*
chaz_Util_strdup(const char *string) {
    size_t len = strlen(string);
    char *copy = (char*)malloc(len + 1);
    strncpy(copy, string, len);
    copy[len] = '\0';
    return copy;
}

char*
chaz_Util_join(const char *sep, ...) {
    va_list args;
    char *result;

    va_start(args, sep);
    result = chaz_Util_vjoin(sep, args);
    va_end(args);

    return result;
}

char*
chaz_Util_vjoin(const char *sep, va_list orig_args) {
    va_list args;
    const char *string;
    char *result, *p;
    size_t sep_len = strlen(sep);
    size_t size;
    int i;

    /* Determine result size. */
    va_copy(args, orig_args);
    size = 1;
    string = va_arg(args, const char*);
    for (i = 0; string; ++i) {
        if (i != 0) { size += sep_len; }
        size += strlen(string);
        string = va_arg(args, const char*);
    }
    va_end(args);

    result = (char*)malloc(size);

    /* Create result string. */
    va_copy(args, orig_args);
    p = result;
    string = va_arg(args, const char*);
    for (i = 0; string; ++i) {
        size_t len;
        if (i != 0) {
            memcpy(p, sep, sep_len);
            p += sep_len;
        }
        len = strlen(string);
        memcpy(p, string, len);
        p += len;
        string = va_arg(args, const char*);
    }
    va_end(args);

    *p = '\0';

    return result;
}

void
chaz_Util_die(const char* format, ...) {
    va_list args;
    va_start(args, format);
    vfprintf(stderr, format, args);
    va_end(args);
    fprintf(stderr, "\n");
    exit(1);
}

void
chaz_Util_warn(const char* format, ...) {
    va_list args;
    va_start(args, format);
    vfprintf(stderr, format, args);
    va_end(args);
    fprintf(stderr, "\n");
}

int
chaz_Util_remove_and_verify(const char *file_path) {
    /* Attempt to delete the file.  If it's gone after the attempt, return
     * success, whether or not it was there to begin with.
     * (ENOENT is POSIX not C89, but let's go with it for now.) */
    int result = chaz_OS_remove(file_path);
    if (result || errno == ENOENT) {
        return 1;
    }

    /* Issue a warning and return failure. */
    chaz_Util_warn("Failed to remove '%s': %s at %s line %d",
                   file_path, strerror(errno), __FILE__, __LINE__);
    return 0;
}

int
chaz_Util_can_open_file(const char *file_path) {
    FILE *garbage_fh;

    /* Use fopen as a portable test for the existence of a file. */
    garbage_fh = fopen(file_path, "rb");
    if (garbage_fh == NULL) {
        return 0;
    }
    else {
        fclose(garbage_fh);
        return 1;
    }
}



/***************************************************************************/

#line 17 "src/Charmonizer/Probe.c"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
/* #include "Charmonizer/Probe.h" */
/* #include "Charmonizer/Core/HeaderChecker.h" */
/* #include "Charmonizer/Core/ConfWriter.h" */
/* #include "Charmonizer/Core/ConfWriterC.h" */
/* #include "Charmonizer/Core/ConfWriterPerl.h" */
/* #include "Charmonizer/Core/ConfWriterPython.h" */
/* #include "Charmonizer/Core/ConfWriterRuby.h" */
/* #include "Charmonizer/Core/Util.h" */
/* #include "Charmonizer/Core/CLI.h" */
/* #include "Charmonizer/Core/Compiler.h" */
/* #include "Charmonizer/Core/Make.h" */
/* #include "Charmonizer/Core/OperatingSystem.h" */

int
chaz_Probe_parse_cli_args(int argc, const char *argv[], chaz_CLI *cli) {
    int i;

    /* Register Charmonizer-specific options. */
    chaz_CLI_register(cli, "enable-c", "generate charmony.h", CHAZ_CLI_NO_ARG);
    chaz_CLI_register(cli, "enable-perl", "generate Charmony.pm", CHAZ_CLI_NO_ARG);
    chaz_CLI_register(cli, "enable-python", "generate charmony.py", CHAZ_CLI_NO_ARG);
    chaz_CLI_register(cli, "enable-ruby", "generate charmony.rb", CHAZ_CLI_NO_ARG);
    chaz_CLI_register(cli, "enable-makefile", NULL, CHAZ_CLI_NO_ARG);
    chaz_CLI_register(cli, "enable-coverage", NULL, CHAZ_CLI_NO_ARG);
    chaz_CLI_register(cli, "cc", "compiler command", CHAZ_CLI_ARG_REQUIRED);
    chaz_CLI_register(cli, "cflags", NULL, CHAZ_CLI_ARG_OPTIONAL);
    chaz_CLI_register(cli, "make", "make command", CHAZ_CLI_ARG_OPTIONAL);

    /* Parse options, exiting on failure. */
    if (!chaz_CLI_parse(cli, argc, argv)) {
        fprintf(stderr, "%s", chaz_CLI_help(cli));
        exit(1);
    }

    /* Accumulate compiler flags. */
    {
        char *cflags = chaz_Util_strdup("");
        size_t cflags_len = 0;
        for (i = 0; i < argc; i++) {
            if (strcmp(argv[i], "--") == 0) {
                i++;
                break;
            }
        }
        for (; i < argc; i++) {
            const char *arg = argv[i];
            cflags_len += strlen(arg) + 2;
            cflags = (char*)realloc(cflags, cflags_len);
            strcat(cflags, " ");
            strcat(cflags, arg);
        }
        chaz_CLI_set(cli, "cflags", cflags);
        free(cflags);
    }

    /* Some Perl setups have a 'cc' config value with leading whitespace. */
    if (chaz_CLI_defined(cli, "cc")) {
        const char *arg = chaz_CLI_strval(cli, "cc");
        char  *cc;
        size_t len = strlen(arg);
        size_t l   = 0;
        size_t r   = len;
        size_t trimmed_len;

        while (isspace((unsigned char)arg[l])) {
            ++l;
        }
        while (r > l && isspace((unsigned char)arg[r-1])) {
            --r;
        }

        trimmed_len = r - l;
        cc = (char*)malloc(trimmed_len + 1);
        memcpy(cc, arg + l, trimmed_len);
        cc[trimmed_len] = '\0';
        chaz_CLI_unset(cli, "cc");
        chaz_CLI_set(cli, "cc", cc);
        free(cc);
    }

    /* Validate. */
    if (!chaz_CLI_defined(cli, "cc")
        || !strlen(chaz_CLI_strval(cli, "cc"))
       ) {
        return false;
    }

    return true;
}

void
chaz_Probe_die_usage(void) {
    fprintf(stderr,
            "Usage: ./charmonize --cc=CC_COMMAND [--enable-c] "
            "[--enable-perl] [--enable-python] [--enable-ruby] -- CFLAGS\n");
    exit(1);
}

void
chaz_Probe_init(struct chaz_CLI *cli) {
    int output_enabled = 0;

    {
        /* Process CHARM_VERBOSITY environment variable. */
        const char *verbosity_env = getenv("CHARM_VERBOSITY");
        if (verbosity_env && strlen(verbosity_env)) {
            chaz_Util_verbosity = strtol(verbosity_env, NULL, 10);
        }
    }

    /* Dispatch other initializers. */
    chaz_OS_init();
    chaz_CC_init(chaz_CLI_strval(cli, "cc"), chaz_CLI_strval(cli, "cflags"));
    chaz_ConfWriter_init();
    chaz_HeadCheck_init();
    chaz_Make_init(chaz_CLI_strval(cli, "make"));

    /* Enable output. */
    if (chaz_CLI_defined(cli, "enable-c")) {
        chaz_ConfWriterC_enable();
        output_enabled = true;
    }
    if (chaz_CLI_defined(cli, "enable-perl")) {
        chaz_ConfWriterPerl_enable();
        output_enabled = true;
    }
    if (chaz_CLI_defined(cli, "enable-python")) {
        chaz_ConfWriterPython_enable();
        output_enabled = true;
    }
    if (chaz_CLI_defined(cli, "enable-ruby")) {
        chaz_ConfWriterRuby_enable();
        output_enabled = true;
    }
    if (!output_enabled) {
        fprintf(stderr, "No output formats enabled\n");
        exit(1);
    }

    if (chaz_Util_verbosity) { printf("Initialization complete.\n"); }
}

void
chaz_Probe_clean_up(void) {
    if (chaz_Util_verbosity) { printf("Cleaning up...\n"); }

    /* Dispatch various clean up routines. */
    chaz_ConfWriter_clean_up();
    chaz_CC_clean_up();
    chaz_Make_clean_up();

    if (chaz_Util_verbosity) { printf("Cleanup complete.\n"); }
}

int
chaz_Probe_gcc_version_num(void) {
    return chaz_CC_gcc_version_num();
}

const char*
chaz_Probe_gcc_version(void) {
    return chaz_CC_gcc_version_num() ? chaz_CC_gcc_version() : NULL;
}

int
chaz_Probe_msvc_version_num(void) {
    return chaz_CC_msvc_version_num();
}

/***************************************************************************/

#line 17 "src/Charmonizer/Probe/AtomicOps.c"
/* #include "Charmonizer/Core/Compiler.h" */
/* #include "Charmonizer/Core/HeaderChecker.h" */
/* #include "Charmonizer/Core/ConfWriter.h" */
/* #include "Charmonizer/Core/Util.h" */
/* #include "Charmonizer/Probe/AtomicOps.h" */
#include <string.h>
#include <stdio.h>
#include <stdlib.h>


static int
chaz_AtomicOps_osatomic_cas_ptr(void) {
    static const char osatomic_casptr_code[] =
        CHAZ_QUOTE(  #include <libkern/OSAtomic.h>                                  )
        CHAZ_QUOTE(  #include <libkern/OSAtomic.h>                                  )
        CHAZ_QUOTE(  int main() {                                                   )
        CHAZ_QUOTE(      int  foo = 1;                                              )
        CHAZ_QUOTE(      int *foo_ptr = &foo;                                       )
        CHAZ_QUOTE(      int *target = NULL;                                        )
        CHAZ_QUOTE(      OSAtomicCompareAndSwapPtr(NULL, foo_ptr, (void**)&target); )
        CHAZ_QUOTE(      return 0;                                                  )
        CHAZ_QUOTE(  }                                                              );
     return chaz_CC_test_compile(osatomic_casptr_code);
}

void
chaz_AtomicOps_run(void) {
    chaz_ConfWriter_start_module("AtomicOps");

    if (chaz_HeadCheck_check_header("libkern/OSAtomic.h")) {
        chaz_ConfWriter_add_def("HAS_LIBKERN_OSATOMIC_H", NULL);

        /* Check for OSAtomicCompareAndSwapPtr, introduced in later versions
         * of OSAtomic.h. */
        if (chaz_AtomicOps_osatomic_cas_ptr()) {
            chaz_ConfWriter_add_def("HAS_OSATOMIC_CAS_PTR", NULL);
        }
    }
    if (chaz_HeadCheck_check_header("sys/atomic.h")) {
        chaz_ConfWriter_add_def("HAS_SYS_ATOMIC_H", NULL);
    }
    if (chaz_HeadCheck_check_header("windows.h")
        && chaz_HeadCheck_check_header("intrin.h")
       ) {
        chaz_ConfWriter_add_def("HAS_INTRIN_H", NULL);
    }

    chaz_ConfWriter_end_module();
}




/***************************************************************************/

#line 17 "src/Charmonizer/Probe/Booleans.c"
/* #include "Charmonizer/Core/HeaderChecker.h" */
/* #include "Charmonizer/Core/ConfWriter.h" */
/* #include "Charmonizer/Probe/Booleans.h" */

void
chaz_Booleans_run(void) {
    int has_stdbool = chaz_HeadCheck_check_header("stdbool.h");

    chaz_ConfWriter_start_module("Booleans");

    if (has_stdbool) {
        chaz_ConfWriter_add_def("HAS_STDBOOL_H", NULL);
        chaz_ConfWriter_add_sys_include("stdbool.h");
    }
    else {
        chaz_ConfWriter_append_conf(
            "#if (defined(CHY_EMPLOY_BOOLEANS) && !defined(__cplusplus))\n"
            "  typedef int bool;\n"
            "  #ifndef true\n"
            "    #define true 1\n"
            "  #endif\n"
            "  #ifndef false\n"
            "    #define false 0\n"
            "  #endif\n"
            "#endif\n");
    }

    chaz_ConfWriter_end_module();
}



/***************************************************************************/

#line 17 "src/Charmonizer/Probe/BuildEnv.c"
/* #include "Charmonizer/Core/HeaderChecker.h" */
/* #include "Charmonizer/Core/CFlags.h" */
/* #include "Charmonizer/Core/Compiler.h" */
/* #include "Charmonizer/Core/ConfWriter.h" */
/* #include "Charmonizer/Probe/BuildEnv.h" */

void
chaz_BuildEnv_run(void) {
    chaz_CFlags *extra_cflags = chaz_CC_get_extra_cflags();
    const char  *extra_cflags_string = chaz_CFlags_get_string(extra_cflags);

    chaz_ConfWriter_start_module("BuildEnv");

    chaz_ConfWriter_add_def("CC", chaz_CC_get_cc());
    chaz_ConfWriter_add_def("CFLAGS", chaz_CC_get_cflags());
    chaz_ConfWriter_add_def("EXTRA_CFLAGS", extra_cflags_string);

    chaz_ConfWriter_end_module();
}



/***************************************************************************/

#line 17 "src/Charmonizer/Probe/DirManip.c"
/* #include "Charmonizer/Core/ConfWriter.h" */
/* #include "Charmonizer/Core/Compiler.h" */
/* #include "Charmonizer/Core/OperatingSystem.h" */
/* #include "Charmonizer/Core/Util.h" */
/* #include "Charmonizer/Core/HeaderChecker.h" */
/* #include "Charmonizer/Probe/DirManip.h" */
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

static struct {
    int  mkdir_num_args;
    char mkdir_command[7];
} chaz_DirManip = { 0, "" };

/* Source code for rmdir. */
static int
chaz_DirManip_compile_posix_mkdir(const char *header) {
    static const char posix_mkdir_code[] =
        CHAZ_QUOTE(  #include <%s>                                      )
        CHAZ_QUOTE(  int main(int argc, char **argv) {                  )
        CHAZ_QUOTE(      if (argc != 2) { return 1; }                   )
        CHAZ_QUOTE(      if (mkdir(argv[1], 0777) != 0) { return 2; }   )
        CHAZ_QUOTE(      return 0;                                      )
        CHAZ_QUOTE(  }                                                  );
    char code_buf[sizeof(posix_mkdir_code) + 30];
    int mkdir_available;
    if (strlen(header) > 25) {
        chaz_Util_die("Header name too long: '%s'", header);
    }

    /* Attempt compilation. */
    sprintf(code_buf, posix_mkdir_code, header);
    mkdir_available = chaz_CC_test_compile(code_buf);

    /* Set vars on success. */
    if (mkdir_available) {
        strcpy(chaz_DirManip.mkdir_command, "mkdir");
        if (strcmp(header, "direct.h") == 0) {
            chaz_DirManip.mkdir_num_args = 1;
        }
        else {
            chaz_DirManip.mkdir_num_args = 2;
        }
    }

    return mkdir_available;
}

static int
chaz_DirManip_compile_win_mkdir(void) {
    static const char win_mkdir_code[] =
        CHAZ_QUOTE(  #include <direct.h>                                )
        CHAZ_QUOTE(  int main(int argc, char **argv) {                  )
        CHAZ_QUOTE(      if (argc != 2) { return 1; }                   )
        CHAZ_QUOTE(      if (_mkdir(argv[1]) != 0) { return 2; }        )
        CHAZ_QUOTE(      return 0;                                      )
        CHAZ_QUOTE(  }                                                  );
    int mkdir_available;

    mkdir_available = chaz_CC_test_compile(win_mkdir_code);
    if (mkdir_available) {
        strcpy(chaz_DirManip.mkdir_command, "_mkdir");
        chaz_DirManip.mkdir_num_args = 1;
    }
    return mkdir_available;
}

static void
chaz_DirManip_try_mkdir(void) {
    if (chaz_HeadCheck_check_header("windows.h")) {
        if (chaz_DirManip_compile_win_mkdir())               { return; }
        if (chaz_DirManip_compile_posix_mkdir("direct.h"))   { return; }
    }
    if (chaz_DirManip_compile_posix_mkdir("sys/stat.h")) { return; }
}

static int
chaz_DirManip_compile_rmdir(const char *header) {
    static const char rmdir_code[] =
        CHAZ_QUOTE(  #include <%s>                                      )
        CHAZ_QUOTE(  int main(int argc, char **argv) {                  )
        CHAZ_QUOTE(      if (argc != 2) { return 1; }                   )
        CHAZ_QUOTE(      if (rmdir(argv[1]) != 0) { return 2; }         )
        CHAZ_QUOTE(      return 0;                                      )
        CHAZ_QUOTE(  }                                                  );
    char code_buf[sizeof(rmdir_code) + 30];
    int rmdir_available;
    if (strlen(header) > 25) {
        chaz_Util_die("Header name too long: '%s'", header);
    }
    sprintf(code_buf, rmdir_code, header);
    rmdir_available = chaz_CC_test_compile(code_buf);
    return rmdir_available;
}

static void
chaz_DirManip_try_rmdir(void) {
    if (chaz_DirManip_compile_rmdir("unistd.h"))   { return; }
    if (chaz_DirManip_compile_rmdir("dirent.h"))   { return; }
    if (chaz_DirManip_compile_rmdir("direct.h"))   { return; }
}

void
chaz_DirManip_run(void) {
    int has_dirent_h = chaz_HeadCheck_check_header("dirent.h");
    int has_direct_h = chaz_HeadCheck_check_header("direct.h");
    int has_dirent_d_namlen = false;
    int has_dirent_d_type   = false;

    chaz_ConfWriter_start_module("DirManip");
    chaz_DirManip_try_mkdir();
    chaz_DirManip_try_rmdir();

    /* Header checks. */
    if (has_dirent_h) {
        chaz_ConfWriter_add_def("HAS_DIRENT_H", NULL);
    }
    if (has_direct_h) {
        chaz_ConfWriter_add_def("HAS_DIRECT_H", NULL);
    }

    /* Check for members in struct dirent. */
    if (has_dirent_h) {
        has_dirent_d_namlen = chaz_HeadCheck_contains_member(
                                  "struct dirent", "d_namlen",
                                  "#include <sys/types.h>\n#include <dirent.h>"
                              );
        if (has_dirent_d_namlen) {
            chaz_ConfWriter_add_def("HAS_DIRENT_D_NAMLEN", NULL);
        }
        has_dirent_d_type = chaz_HeadCheck_contains_member(
                                "struct dirent", "d_type",
                                "#include <sys/types.h>\n#include <dirent.h>"
                            );
        if (has_dirent_d_type) {
            chaz_ConfWriter_add_def("HAS_DIRENT_D_TYPE", NULL);
        }
    }

    if (chaz_DirManip.mkdir_num_args == 2) {
        /* It's two args, but the command isn't "mkdir". */
        char scratch[50];
        if (strlen(chaz_DirManip.mkdir_command) > 30) {
            chaz_Util_die("Command too long: '%s'", chaz_DirManip.mkdir_command);
        }
        sprintf(scratch, "%s(_dir, _mode)", chaz_DirManip.mkdir_command);
        chaz_ConfWriter_add_def("makedir(_dir, _mode)", scratch);
        chaz_ConfWriter_add_def("MAKEDIR_MODE_IGNORED", "0");
    }
    else if (chaz_DirManip.mkdir_num_args == 1) {
        /* It's one arg... mode arg will be ignored. */
        char scratch[50];
        if (strlen(chaz_DirManip.mkdir_command) > 30) {
            chaz_Util_die("Command too long: '%s'", chaz_DirManip.mkdir_command);
        }
        sprintf(scratch, "%s(_dir)", chaz_DirManip.mkdir_command);
        chaz_ConfWriter_add_def("makedir(_dir, _mode)", scratch);
        chaz_ConfWriter_add_def("MAKEDIR_MODE_IGNORED", "1");
    }

    if (chaz_CC_has_macro("_WIN32") && !chaz_CC_is_cygwin()) {
        chaz_ConfWriter_add_def("DIR_SEP", "\"\\\\\"");
        chaz_ConfWriter_add_def("DIR_SEP_CHAR", "'\\\\'");
    }
    else {
        chaz_ConfWriter_add_def("DIR_SEP", "\"/\"");
        chaz_ConfWriter_add_def("DIR_SEP_CHAR", "'/'");
    }

    /* See whether remove works on directories. */
    chaz_OS_mkdir("_charm_test_remove_me");
    if (0 == remove("_charm_test_remove_me")) {
        chaz_ConfWriter_add_def("REMOVE_ZAPS_DIRS", NULL);
    }
    chaz_OS_rmdir("_charm_test_remove_me");

    chaz_ConfWriter_end_module();
}




/***************************************************************************/

#line 17 "src/Charmonizer/Probe/Floats.c"
/* #include "Charmonizer/Core/HeaderChecker.h" */
/* #include "Charmonizer/Core/CFlags.h" */
/* #include "Charmonizer/Core/Compiler.h" */
/* #include "Charmonizer/Core/ConfWriter.h" */
/* #include "Charmonizer/Core/Util.h" */
/* #include "Charmonizer/Probe/Floats.h" */
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

void
chaz_Floats_run(void) {
    chaz_ConfWriter_start_module("Floats");

    chaz_ConfWriter_append_conf(
        "typedef union { unsigned char c[4]; float f; } chy_floatu32;\n"
        "typedef union { unsigned char c[8]; double d; } chy_floatu64;\n"
        "#ifdef CHY_BIG_END\n"
        "static const chy_floatu32 chy_f32inf\n"
        "    = { { 0x7F, 0x80, 0, 0 } };\n"
        "static const chy_floatu32 chy_f32neginf\n"
        "    = { { 0xFF, 0x80, 0, 0 } };\n"
        "static const chy_floatu32 chy_f32nan\n"
        "    = { { 0x7F, 0xC0, 0, 0 } };\n"
        "static const chy_floatu64 chy_f64inf\n"
        "    = { { 0x7F, 0xF0, 0, 0, 0, 0, 0, 0 } };\n"
        "static const chy_floatu64 chy_f64neginf\n"
        "    = { { 0xFF, 0xF0, 0, 0, 0, 0, 0, 0 } };\n"
        "static const chy_floatu64 chy_f64nan\n"
        "    = { { 0x7F, 0xF8, 0, 0, 0, 0, 0, 0 } };\n"
        "#else /* BIG_END */\n"
        "static const chy_floatu32 chy_f32inf\n"
        "    = { { 0, 0, 0x80, 0x7F } };\n"
        "static const chy_floatu32 chy_f32neginf\n"
        "    = { { 0, 0, 0x80, 0xFF } };\n"
        "static const chy_floatu32 chy_f32nan\n"
        "    = { { 0, 0, 0xC0, 0x7F } };\n"
        "static const chy_floatu64 chy_f64inf\n"
        "    = { { 0, 0, 0, 0, 0, 0, 0xF0, 0x7F } };\n"
        "static const chy_floatu64 chy_f64neginf\n"
        "    = { { 0, 0, 0, 0, 0, 0, 0xF0, 0xFF } };\n"
        "static const chy_floatu64 chy_f64nan\n"
        "    = { { 0, 0, 0, 0, 0, 0, 0xF8, 0x7F } };\n"
        "#endif /* BIG_END */\n"
    );
    chaz_ConfWriter_add_def("F32_INF", "(chy_f32inf.f)");
    chaz_ConfWriter_add_def("F32_NEGINF", "(chy_f32neginf.f)");
    chaz_ConfWriter_add_def("F32_NAN", "(chy_f32nan.f)");
    chaz_ConfWriter_add_def("F64_INF", "(chy_f64inf.d)");
    chaz_ConfWriter_add_def("F64_NEGINF", "(chy_f64neginf.d)");
    chaz_ConfWriter_add_def("F64_NAN", "(chy_f64nan.d)");

    chaz_ConfWriter_end_module();
}

const char*
chaz_Floats_math_library(void) {
    static const char sqrt_code[] =
        CHAZ_QUOTE(  #include <math.h>                              )
        CHAZ_QUOTE(  #include <stdio.h>                             )
        CHAZ_QUOTE(  typedef double (*sqrt_t)(double);              )
        CHAZ_QUOTE(  int main(void) {                               )
        CHAZ_QUOTE(      printf("%p\n", (sqrt_t)sqrt);              )
        CHAZ_QUOTE(      return 0;                                  )
        CHAZ_QUOTE(  }                                              );
    chaz_CFlags *temp_cflags = chaz_CC_get_temp_cflags();
    char        *output = NULL;
    size_t       output_len;

    output = chaz_CC_capture_output(sqrt_code, &output_len);
    if (output != NULL) {
        /* Linking against libm not needed. */
        free(output);
        return NULL;
    }

    chaz_CFlags_add_external_lib(temp_cflags, "m");
    output = chaz_CC_capture_output(sqrt_code, &output_len);
    chaz_CFlags_clear(temp_cflags);

    if (output == NULL) {
        chaz_Util_die("Don't know how to use math library.");
    }

    free(output);
    return "m";
}



/***************************************************************************/

#line 17 "src/Charmonizer/Probe/FuncMacro.c"
/* #include "Charmonizer/Core/Compiler.h" */
/* #include "Charmonizer/Core/ConfWriter.h" */
/* #include "Charmonizer/Core/Util.h" */
/* #include "Charmonizer/Probe/FuncMacro.h" */
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

/* Probe for ISO func macro. */
static int
chaz_FuncMacro_probe_iso() {
    static const char iso_func_code[] =
        CHAZ_QUOTE(  #include <stdio.h>                )
        CHAZ_QUOTE(  int main() {                      )
        CHAZ_QUOTE(      printf("%s", __func__);       )
        CHAZ_QUOTE(      return 0;                     )
        CHAZ_QUOTE(  }                                 );
    size_t output_len;
    char *output;
    int success = false;

    output = chaz_CC_capture_output(iso_func_code, &output_len);
    if (output != NULL && strncmp(output, "main", 4) == 0) {
        success = true;
    }
    free(output);

    return success;
}

static int
chaz_FuncMacro_probe_gnu() {
    /* Code for verifying GNU func macro. */
    static const char gnu_func_code[] =
        CHAZ_QUOTE(  #include <stdio.h>                )
        CHAZ_QUOTE(  int main() {                      )
        CHAZ_QUOTE(      printf("%s", __FUNCTION__);   )
        CHAZ_QUOTE(      return 0;                     )
        CHAZ_QUOTE(  }                                 );
    size_t output_len;
    char *output;
    int success = false;

    output = chaz_CC_capture_output(gnu_func_code, &output_len);
    if (output != NULL && strncmp(output, "main", 4) == 0) {
        success = true;
    }
    free(output);

    return success;
}

/* Attempt to verify inline keyword. */
static char*
chaz_FuncMacro_try_inline(const char *keyword, size_t *output_len) {
    static const char inline_code[] =
        CHAZ_QUOTE(  #include <stdio.h>                )
        CHAZ_QUOTE(  static %s int foo() { return 1; } )
        CHAZ_QUOTE(  int main() {                      )
        CHAZ_QUOTE(      printf("%%d", foo());         )
        CHAZ_QUOTE(      return 0;                     )
        CHAZ_QUOTE(  }                                 );
    char code[sizeof(inline_code) + 30];
    sprintf(code, inline_code, keyword);
    return chaz_CC_capture_output(code, output_len);
}

static void
chaz_FuncMacro_probe_inline(void) {
    static const char* inline_options[] = {
        "__inline",
        "__inline__",
        "inline"
    };
    const int num_inline_options = sizeof(inline_options) / sizeof(void*);
    int has_inline = false;
    int i;

    for (i = 0; i < num_inline_options; i++) {
        const char *inline_option = inline_options[i];
        size_t output_len;
        char *output = chaz_FuncMacro_try_inline(inline_option, &output_len);
        if (output != NULL) {
            has_inline = true;
            chaz_ConfWriter_add_def("INLINE", inline_option);
            free(output);
            break;
        }
    }
    if (!has_inline) {
        chaz_ConfWriter_add_def("INLINE", NULL);
    }
}

void
chaz_FuncMacro_run(void) {
    int has_funcmac      = false;
    int has_iso_funcmac  = false;
    int has_gnuc_funcmac = false;

    chaz_ConfWriter_start_module("FuncMacro");

    /* Check for func macros. */
    if (chaz_FuncMacro_probe_iso()) {
        has_funcmac     = true;
        has_iso_funcmac = true;
    }
    if (chaz_FuncMacro_probe_gnu()) {
        has_funcmac      = true;
        has_gnuc_funcmac = true;
    }

    /* Write out common defines. */
    if (has_funcmac) {
        const char *macro_text = has_iso_funcmac
                                 ? "__func__"
                                 : "__FUNCTION__";
        chaz_ConfWriter_add_def("HAS_FUNC_MACRO", NULL);
        chaz_ConfWriter_add_def("FUNC_MACRO", macro_text);
    }

    /* Write out specific defines. */
    if (has_iso_funcmac) {
        chaz_ConfWriter_add_def("HAS_ISO_FUNC_MACRO", NULL);
    }
    if (has_gnuc_funcmac) {
        chaz_ConfWriter_add_def("HAS_GNUC_FUNC_MACRO", NULL);
    }

    /* Check for inline keyword. */
    chaz_FuncMacro_probe_inline();

    chaz_ConfWriter_end_module();
}




/***************************************************************************/

#line 17 "src/Charmonizer/Probe/Headers.c"
/* #include "Charmonizer/Core/HeaderChecker.h" */
/* #include "Charmonizer/Core/ConfWriter.h" */
/* #include "Charmonizer/Core/Util.h" */
/* #include "Charmonizer/Probe/Headers.h" */
#include <ctype.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>

#define CHAZ_HEADERS_MAX_KEEPERS 200

static struct {
    int keeper_count;
    const char *keepers[CHAZ_HEADERS_MAX_KEEPERS + 1];
} chaz_Headers = { 0, { NULL } };

/* Add a header to the keepers array.
 */
static void
chaz_Headers_keep(const char *header_name);

/* Transform "header.h" into "CHY_HAS_HEADER_H, storing the result into
 * `buffer`.
 */
static void
chaz_Headers_encode_affirmation(const char *header_name, char *buffer,
                                size_t buf_size);

/* Probe for all C89 headers. */
static void
chaz_Headers_probe_c89(void);

/* Probe for all POSIX headers. */
static void
chaz_Headers_probe_posix(void);

/* Prove for selected Windows headers. */
static void
chaz_Headers_probe_win(void);

int
chaz_Headers_check(const char *header_name) {
    return chaz_HeadCheck_check_header(header_name);
}

void
chaz_Headers_run(void) {
    int i;

    chaz_ConfWriter_start_module("Headers");

    chaz_Headers_probe_posix();
    chaz_Headers_probe_c89();
    chaz_Headers_probe_win();

    /* One-offs. */
    if (chaz_HeadCheck_check_header("pthread.h")) {
        chaz_Headers_keep("pthread.h");
    }

    /* Append the config with every header detected so far. */
    for (i = 0; chaz_Headers.keepers[i] != NULL; i++) {
        char aff_buf[200];
        chaz_Headers_encode_affirmation(chaz_Headers.keepers[i], aff_buf, 200);
        chaz_ConfWriter_add_def(aff_buf, NULL);
    }

    chaz_ConfWriter_end_module();
}

static void
chaz_Headers_keep(const char *header_name) {
    if (chaz_Headers.keeper_count >= CHAZ_HEADERS_MAX_KEEPERS) {
        chaz_Util_die("Too many keepers -- increase MAX_KEEPER_COUNT");
    }
    chaz_Headers.keepers[chaz_Headers.keeper_count++] = header_name;
    chaz_Headers.keepers[chaz_Headers.keeper_count]   = NULL;
}

static void
chaz_Headers_encode_affirmation(const char *header_name, char *buffer, size_t buf_size) {
    char *buf, *buf_end;
    size_t len = strlen(header_name) + sizeof("HAS_");
    if (len + 1 > buf_size) {
        chaz_Util_die("Buffer too small: %lu", (unsigned long)buf_size);
    }

    /* Start off with "HAS_". */
    strcpy(buffer, "HAS_");

    /* Transform one char at a time. */
    for (buf = buffer + sizeof("HAS_") - 1, buf_end = buffer + len;
         buf < buf_end;
         header_name++, buf++
        ) {
        if (*header_name == '\0') {
            *buf = '\0';
            break;
        }
        else if (isalnum((unsigned char)*header_name)) {
            *buf = toupper((unsigned char)*header_name);
        }
        else {
            *buf = '_';
        }
    }
}

static void
chaz_Headers_probe_c89(void) {
    const char *c89_headers[] = {
        "assert.h",
        "ctype.h",
        "errno.h",
        "float.h",
        "limits.h",
        "locale.h",
        "math.h",
        "setjmp.h",
        "signal.h",
        "stdarg.h",
        "stddef.h",
        "stdio.h",
        "stdlib.h",
        "string.h",
        "time.h",
        NULL
    };
    int i;

    /* Test for all c89 headers in one blast. */
    if (chaz_HeadCheck_check_many_headers((const char**)c89_headers)) {
        chaz_ConfWriter_add_def("HAS_C89", NULL);
        chaz_ConfWriter_add_def("HAS_C90", NULL);
        for (i = 0; c89_headers[i] != NULL; i++) {
            chaz_Headers_keep(c89_headers[i]);
        }
    }
    /* Test one-at-a-time. */
    else {
        for (i = 0; c89_headers[i] != NULL; i++) {
            if (chaz_HeadCheck_check_header(c89_headers[i])) {
                chaz_Headers_keep(c89_headers[i]);
            }
        }
    }
}

static void
chaz_Headers_probe_posix(void) {
    const char *posix_headers[] = {
        "cpio.h",
        "dirent.h",
        "fcntl.h",
        "grp.h",
        "pwd.h",
        "regex.h",
        "sched.h",
        "sys/stat.h",
        "sys/time.h",
        "sys/times.h",
        "sys/types.h",
        "sys/utsname.h",
        "sys/wait.h",
        "tar.h",
        "termios.h",
        "unistd.h",
        "utime.h",
        NULL
    };
    int i;

    /* Try for all POSIX headers in one blast. */
    if (chaz_HeadCheck_check_many_headers((const char**)posix_headers)) {
        chaz_ConfWriter_add_def("HAS_POSIX", NULL);
        for (i = 0; posix_headers[i] != NULL; i++) {
            chaz_Headers_keep(posix_headers[i]);
        }
    }
    /* Test one-at-a-time. */
    else {
        for (i = 0; posix_headers[i] != NULL; i++) {
            if (chaz_HeadCheck_check_header(posix_headers[i])) {
                chaz_Headers_keep(posix_headers[i]);
            }
        }
    }
}


static void
chaz_Headers_probe_win(void) {
    const char *win_headers[] = {
        "io.h",
        "windows.h",
        "process.h",
        NULL
    };
    int i;

    /* Test for all Windows headers in one blast */
    if (chaz_HeadCheck_check_many_headers((const char**)win_headers)) {
        for (i = 0; win_headers[i] != NULL; i++) {
            chaz_Headers_keep(win_headers[i]);
        }
    }
    /* Test one-at-a-time. */
    else {
        for (i = 0; win_headers[i] != NULL; i++) {
            if (chaz_HeadCheck_check_header(win_headers[i])) {
                chaz_Headers_keep(win_headers[i]);
            }
        }
    }
}


/***************************************************************************/

#line 17 "src/Charmonizer/Probe/Integers.c"
/* #include "Charmonizer/Core/HeaderChecker.h" */
/* #include "Charmonizer/Core/Compiler.h" */
/* #include "Charmonizer/Core/ConfWriter.h" */
/* #include "Charmonizer/Core/Util.h" */
/* #include "Charmonizer/Probe/Integers.h" */
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

/* Determine endian-ness of this machine.
 */
static int
chaz_Integers_machine_is_big_endian(void);

static const char chaz_Integers_sizes_code[] =
    CHAZ_QUOTE(  #include <stdio.h>                        )
    CHAZ_QUOTE(  int main () {                             )
    CHAZ_QUOTE(      printf("%d ", (int)sizeof(char));     )
    CHAZ_QUOTE(      printf("%d ", (int)sizeof(short));    )
    CHAZ_QUOTE(      printf("%d ", (int)sizeof(int));      )
    CHAZ_QUOTE(      printf("%d ", (int)sizeof(long));     )
    CHAZ_QUOTE(      printf("%d ", (int)sizeof(void*));    )
    CHAZ_QUOTE(      printf("%d ", (int)sizeof(size_t));   )
    CHAZ_QUOTE(      return 0;                             )
    CHAZ_QUOTE(  }                                         );

static const char chaz_Integers_stdint_type_code[] =
    CHAZ_QUOTE(  #include <stdint.h>                       )
    CHAZ_QUOTE(  #include <stdio.h>                        )
    CHAZ_QUOTE(  int main()                                )
    CHAZ_QUOTE(  {                                         )
    CHAZ_QUOTE(      printf("%%d", (int)sizeof(%s));       )
    CHAZ_QUOTE(      return 0;                             )
    CHAZ_QUOTE(  }                                         );

static const char chaz_Integers_type64_code[] =
    CHAZ_QUOTE(  #include <stdio.h>                        )
    CHAZ_QUOTE(  int main()                                )
    CHAZ_QUOTE(  {                                         )
    CHAZ_QUOTE(      printf("%%d", (int)sizeof(%s));       )
    CHAZ_QUOTE(      return 0;                             )
    CHAZ_QUOTE(  }                                         );

static const char chaz_Integers_literal64_code[] =
    CHAZ_QUOTE(  #include <stdio.h>                        )
    CHAZ_QUOTE(  #define big 9000000000000000000%s         )
    CHAZ_QUOTE(  int main()                                )
    CHAZ_QUOTE(  {                                         )
    CHAZ_QUOTE(      int truncated = (int)big;             )
    CHAZ_QUOTE(      printf("%%d\n", truncated);           )
    CHAZ_QUOTE(      return 0;                             )
    CHAZ_QUOTE(  }                                         );

static const char chaz_Integers_u64_to_double_code[] =
    CHAZ_QUOTE(  #include <stdio.h>                        )
    CHAZ_QUOTE(  int main()                                )
    CHAZ_QUOTE(  {                                         )
    CHAZ_QUOTE(      unsigned %s int_num = 0;              )
    CHAZ_QUOTE(      double float_num;                     )
    CHAZ_QUOTE(      float_num = (double)int_num;          )
    CHAZ_QUOTE(      printf("%%f\n", float_num);           )
    CHAZ_QUOTE(      return 0;                             )
    CHAZ_QUOTE(  }                                         );

void
chaz_Integers_run(void) {
    char *output;
    size_t output_len;
    int sizeof_char       = -1;
    int sizeof_short      = -1;
    int sizeof_int        = -1;
    int sizeof_ptr        = -1;
    int sizeof_long       = -1;
    int sizeof_long_long  = -1;
    int sizeof___int64    = -1;
    int sizeof_size_t     = -1;
    int has_8             = false;
    int has_16            = false;
    int has_32            = false;
    int has_64            = false;
    int has_long_long     = false;
    int has___int64       = false;
    int has_intptr_t      = false;
    int has_inttypes      = chaz_HeadCheck_check_header("inttypes.h");
    int has_stdint        = chaz_HeadCheck_check_header("stdint.h");
    char i32_t_type[10];
    char i32_t_postfix[10];
    char u32_t_postfix[10];
    char i64_t_type[10];
    char i64_t_postfix[10];
    char u64_t_postfix[10];
    char printf_modifier_32[10];
    char printf_modifier_64[10];
    char code_buf[1000];
    char scratch[50];

    chaz_ConfWriter_start_module("Integers");

    /* Document endian-ness. */
    if (chaz_Integers_machine_is_big_endian()) {
        chaz_ConfWriter_add_def("BIG_END", NULL);
    }
    else {
        chaz_ConfWriter_add_def("LITTLE_END", NULL);
    }

    /* Record sizeof() for several common integer types. */
    output = chaz_CC_capture_output(chaz_Integers_sizes_code, &output_len);
    if (output != NULL) {
        char *ptr     = output;
        char *end_ptr = output;

        sizeof_char   = strtol(ptr, &end_ptr, 10);
        ptr           = end_ptr;
        sizeof_short  = strtol(ptr, &end_ptr, 10);
        ptr           = end_ptr;
        sizeof_int    = strtol(ptr, &end_ptr, 10);
        ptr           = end_ptr;
        sizeof_long   = strtol(ptr, &end_ptr, 10);
        ptr           = end_ptr;
        sizeof_ptr    = strtol(ptr, &end_ptr, 10);
        ptr           = end_ptr;
        sizeof_size_t = strtol(ptr, &end_ptr, 10);

        free(output);
    }

    /* Determine whether long longs are available. */
    sprintf(code_buf, chaz_Integers_type64_code, "long long");
    output = chaz_CC_capture_output(code_buf, &output_len);
    if (output != NULL) {
        has_long_long    = true;
        sizeof_long_long = strtol(output, NULL, 10);
        free(output);
    }

    /* Determine whether the __int64 type is available. */
    sprintf(code_buf, chaz_Integers_type64_code, "__int64");
    output = chaz_CC_capture_output(code_buf, &output_len);
    if (output != NULL) {
        has___int64 = true;
        sizeof___int64 = strtol(output, NULL, 10);
        free(output);
    }

    /* Determine whether the intptr_t type is available (it's optional in
     * C99). */
    sprintf(code_buf, chaz_Integers_stdint_type_code, "intptr_t");
    output = chaz_CC_capture_output(code_buf, &output_len);
    if (output != NULL) {
        has_intptr_t = true;
        free(output);
    }

    /* Figure out which integer types are available. */
    if (sizeof_char == 1) {
        has_8 = true;
    }
    if (sizeof_short == 2) {
        has_16 = true;
    }
    if (sizeof_int == 4) {
        has_32 = true;
        strcpy(i32_t_type, "int");
        strcpy(i32_t_postfix, "");
        strcpy(u32_t_postfix, "U");
        strcpy(printf_modifier_32, "");
    }
    else if (sizeof_long == 4) {
        has_32 = true;
        strcpy(i32_t_type, "long");
        strcpy(i32_t_postfix, "L");
        strcpy(u32_t_postfix, "UL");
        strcpy(printf_modifier_32, "l");
    }
    if (sizeof_long == 8) {
        has_64 = true;
        strcpy(i64_t_type, "long");
    }
    else if (sizeof_long_long == 8) {
        has_64 = true;
        strcpy(i64_t_type, "long long");
    }
    else if (sizeof___int64 == 8) {
        has_64 = true;
        strcpy(i64_t_type, "__int64");
    }

    /* Probe for 64-bit literal syntax. */
    if (has_64 && sizeof_long == 8) {
        strcpy(i64_t_postfix, "L");
        strcpy(u64_t_postfix, "UL");
    }
    else if (has_64) {
        sprintf(code_buf, chaz_Integers_literal64_code, "LL");
        output = chaz_CC_capture_output(code_buf, &output_len);
        if (output != NULL) {
            strcpy(i64_t_postfix, "LL");
            free(output);
        }
        else {
            sprintf(code_buf, chaz_Integers_literal64_code, "i64");
            output = chaz_CC_capture_output(code_buf, &output_len);
            if (output != NULL) {
                strcpy(i64_t_postfix, "i64");
                free(output);
            }
            else {
                chaz_Util_die("64-bit types, but no literal syntax found");
            }
        }
        sprintf(code_buf, chaz_Integers_literal64_code, "ULL");
        output = chaz_CC_capture_output(code_buf, &output_len);
        if (output != NULL) {
            strcpy(u64_t_postfix, "ULL");
            free(output);
        }
        else {
            sprintf(code_buf, chaz_Integers_literal64_code, "Ui64");
            output = chaz_CC_capture_output(code_buf, &output_len);
            if (output != NULL) {
                strcpy(u64_t_postfix, "Ui64");
                free(output);
            }
            else {
                chaz_Util_die("64-bit types, but no literal syntax found");
            }
        }
    }

    /* Probe for 64-bit printf format string modifier. */
    if (has_64) {
        int i;
        const char *options[] = {
            "ll",
            "l",
            "L",
            "q",   /* Some *BSDs */
            "I64", /* Microsoft */
            NULL,
        };

        /* Buffer to hold the code, and its start and end. */
        static const char format_64_code[] =
            CHAZ_QUOTE(  #include <stdio.h>                            )
            CHAZ_QUOTE(  int main() {                                  )
            CHAZ_QUOTE(      printf("%%%su", 18446744073709551615%s);  )
            CHAZ_QUOTE(      return 0;                                 )
            CHAZ_QUOTE( }                                              );

        for (i = 0; options[i] != NULL; i++) {
            /* Try to print 2**64-1, and see if we get it back intact. */
            int success;
            sprintf(code_buf, format_64_code, options[i], u64_t_postfix);
            output = chaz_CC_capture_output(code_buf, &output_len);
            success = output != NULL
                      && strcmp(output, "18446744073709551615") == 0;
            free(output);

            if (success) {
                break;
            }
        }

        if (options[i] == NULL) {
            chaz_Util_die("64-bit types, but no printf modifier found");
        }

        strcpy(printf_modifier_64, options[i]);
    }

    /* Write out some conditional defines. */
    if (has_inttypes) {
        chaz_ConfWriter_add_def("HAS_INTTYPES_H", NULL);
    }
    if (has_stdint) {
        chaz_ConfWriter_add_def("HAS_STDINT_H", NULL);
    }
    if (has_long_long) {
        chaz_ConfWriter_add_def("HAS_LONG_LONG", NULL);
    }
    if (has___int64) {
        chaz_ConfWriter_add_def("HAS___INT64", NULL);
    }

    /* Write out sizes. */
    sprintf(scratch, "%d", sizeof_char);
    chaz_ConfWriter_add_def("SIZEOF_CHAR", scratch);
    sprintf(scratch, "%d", sizeof_short);
    chaz_ConfWriter_add_def("SIZEOF_SHORT", scratch);
    sprintf(scratch, "%d", sizeof_int);
    chaz_ConfWriter_add_def("SIZEOF_INT", scratch);
    sprintf(scratch, "%d", sizeof_long);
    chaz_ConfWriter_add_def("SIZEOF_LONG", scratch);
    sprintf(scratch, "%d", sizeof_ptr);
    chaz_ConfWriter_add_def("SIZEOF_PTR", scratch);
    sprintf(scratch, "%d", sizeof_size_t);
    chaz_ConfWriter_add_def("SIZEOF_SIZE_T", scratch);
    if (has_long_long) {
        sprintf(scratch, "%d", sizeof_long_long);
        chaz_ConfWriter_add_def("SIZEOF_LONG_LONG", scratch);
    }
    if (has___int64) {
        sprintf(scratch, "%d", sizeof___int64);
        chaz_ConfWriter_add_def("SIZEOF___INT64", scratch);
    }

    /* Write affirmations. */
    if (has_8) {
        chaz_ConfWriter_add_def("HAS_INT8_T", NULL);
    }
    if (has_16) {
        chaz_ConfWriter_add_def("HAS_INT16_T", NULL);
    }
    if (has_32) {
        chaz_ConfWriter_add_def("HAS_INT32_T", NULL);
    }
    if (has_64) {
        chaz_ConfWriter_add_def("HAS_INT64_T", NULL);
    }

    /* Create macro for promoting pointers to integers. */
    if (has_64) {
        if (sizeof_ptr == 8) {
            chaz_ConfWriter_add_def("PTR_TO_I64(ptr)",
                                    "((int64_t)(uint64_t)(ptr))");
        }
        else {
            chaz_ConfWriter_add_def("PTR_TO_I64(ptr)",
                                    "((int64_t)(uint32_t)(ptr))");
        }
    }

    /* Create macro for converting uint64_t to double. */
    if (has_64) {
        /*
         * Determine whether unsigned 64-bit integers can be converted to
         * double. Older MSVC versions don't support this conversion.
         */
        sprintf(code_buf, chaz_Integers_u64_to_double_code, i64_t_type);
        output = chaz_CC_capture_output(code_buf, &output_len);
        if (output != NULL) {
            chaz_ConfWriter_add_def("U64_TO_DOUBLE(num)", "((double)(num))");
            free(output);
        }
        else {
            chaz_ConfWriter_add_def(
                "U64_TO_DOUBLE(num)",
                "((num) & UINT64_C(0x8000000000000000) ? "
                "(double)(int64_t)((num) & UINT64_C(0x7FFFFFFFFFFFFFFF)) + "
                "9223372036854775808.0 : "
                "(double)(int64_t)(num))");
        }
    }

    chaz_ConfWriter_end_module();

    /* Integer typedefs. */

    chaz_ConfWriter_start_module("IntegerTypes");

    if (has_stdint) {
        chaz_ConfWriter_add_sys_include("stdint.h");
    }
    else {
        /* We support only the following subset of stdint.h
         *   int8_t
         *   int16_t
         *   int32_t
         *   int64_t
         *   intmax_t
         *   intptr_t
         *   uint8_t
         *   uint16_t
         *   uint32_t
         *   uint64_t
         *   uintmax_t
         *   uintptr_t
         */
        if (has_8) {
            chaz_ConfWriter_add_global_typedef("signed char", "int8_t");
            chaz_ConfWriter_add_global_typedef("unsigned char", "uint8_t");
        }
        if (has_16) {
            chaz_ConfWriter_add_global_typedef("signed short", "int16_t");
            chaz_ConfWriter_add_global_typedef("unsigned short", "uint16_t");
        }
        if (has_32) {
            chaz_ConfWriter_add_global_typedef(i32_t_type, "int32_t");
            sprintf(scratch, "unsigned %s", i32_t_type);
            chaz_ConfWriter_add_global_typedef(scratch, "uint32_t");
        }
        if (has_64) {
            chaz_ConfWriter_add_global_typedef(i64_t_type, "int64_t");
            sprintf(scratch, "unsigned %s", i64_t_type);
            chaz_ConfWriter_add_global_typedef(scratch, "uint64_t");
        }

        if (has_64) {
            chaz_ConfWriter_add_global_typedef(i64_t_type, "intmax_t");
            sprintf(scratch, "unsigned %s", i64_t_type);
            chaz_ConfWriter_add_global_typedef(scratch, "uintmax_t");
        }
        else if (has_32) {
            chaz_ConfWriter_add_global_typedef(i32_t_type, "intmax_t");
            sprintf(scratch, "unsigned %s", i32_t_type);
            chaz_ConfWriter_add_global_typedef(scratch, "uintmax_t");
        }
    }

    if (!has_intptr_t) {
        if (sizeof_ptr == 4) {
            chaz_ConfWriter_add_global_typedef(i32_t_type, "intptr_t");
            sprintf(scratch, "unsigned %s", i32_t_type);
            chaz_ConfWriter_add_global_typedef(scratch, "uintptr_t");
        }
        else if (sizeof_ptr == 8) {
            chaz_ConfWriter_add_global_typedef(i64_t_type, "intptr_t");
            sprintf(scratch, "unsigned %s", i64_t_type);
            chaz_ConfWriter_add_global_typedef(scratch, "uintptr_t");
        }
    }

    chaz_ConfWriter_end_module();

    /* Integer limits. */

    chaz_ConfWriter_start_module("IntegerLimits");

    if (has_stdint) {
        chaz_ConfWriter_add_sys_include("stdint.h");
    }
    else {
        /* We support only the following subset of stdint.h
         *   INT8_MAX
         *   INT16_MAX
         *   INT32_MAX
         *   INT64_MAX
         *   INTMAX_MAX
         *   INTPTR_MAX
         *   INT8_MIN
         *   INT16_MIN
         *   INT32_MIN
         *   INT64_MIN
         *   INTMAX_MIN
         *   INTPTR_MIN
         *   UINT8_MAX
         *   UINT16_MAX
         *   UINT32_MAX
         *   UINT64_MAX
         *   UINTMAX_MAX
         *   UINTPTR_MAX
         *   SIZE_MAX
         */
        if (has_8) {
            chaz_ConfWriter_add_global_def("INT8_MAX", "127");
            chaz_ConfWriter_add_global_def("INT8_MIN", "-128");
            chaz_ConfWriter_add_global_def("UINT8_MAX", "255");
        }
        if (has_16) {
            chaz_ConfWriter_add_global_def("INT16_MAX", "32767");
            chaz_ConfWriter_add_global_def("INT16_MIN", "-32768");
            chaz_ConfWriter_add_global_def("UINT16_MAX", "65535");
        }
        if (has_32) {
            chaz_ConfWriter_add_global_def("INT32_MAX", "2147483647");
            chaz_ConfWriter_add_global_def("INT32_MIN", "(-2147483647-1)");
            chaz_ConfWriter_add_global_def("UINT32_MAX", "4294967295U");
        }
        if (has_64) {
            sprintf(scratch, "9223372036854775807%s", i64_t_postfix);
            chaz_ConfWriter_add_global_def("INT64_MAX", scratch);
            sprintf(scratch, "(-9223372036854775807%s-1)", i64_t_postfix);
            chaz_ConfWriter_add_global_def("INT64_MIN", scratch);
            sprintf(scratch, "18446744073709551615%s", u64_t_postfix);
            chaz_ConfWriter_add_global_def("UINT64_MAX", scratch);
        }

        if (has_64) {
            sprintf(scratch, "9223372036854775807%s", i64_t_postfix);
            chaz_ConfWriter_add_global_def("INTMAX_MAX", scratch);
            sprintf(scratch, "(-9223372036854775807%s-1)", i64_t_postfix);
            chaz_ConfWriter_add_global_def("INTMAX_MIN", scratch);
            sprintf(scratch, "18446744073709551615%s", u64_t_postfix);
            chaz_ConfWriter_add_global_def("UINTMAX_MAX", scratch);
        }
        else if (has_32) {
            chaz_ConfWriter_add_global_def("INTMAX_MAX", "2147483647");
            chaz_ConfWriter_add_global_def("INTMAX_MIN", "(-2147483647-1)");
            chaz_ConfWriter_add_global_def("UINTMAX_MAX", "4294967295U");
        }

        chaz_ConfWriter_add_global_def("SIZE_MAX", "((size_t)-1)");
    }

    if (!has_intptr_t) {
        if (sizeof_ptr == 4) {
            chaz_ConfWriter_add_global_def("INTPTR_MAX", "2147483647");
            chaz_ConfWriter_add_global_def("INTPTR_MIN", "(-2147483647-1)");
            chaz_ConfWriter_add_global_def("UINTPTR_MAX", "4294967295U");
        }
        else if (sizeof_ptr == 8) {
            sprintf(scratch, "9223372036854775807%s", i64_t_postfix);
            chaz_ConfWriter_add_global_def("INTPTR_MAX", scratch);
            sprintf(scratch, "(-9223372036854775807%s-1)", i64_t_postfix);
            chaz_ConfWriter_add_global_def("INTPTR_MIN", scratch);
            sprintf(scratch, "18446744073709551615%s", u64_t_postfix);
            chaz_ConfWriter_add_global_def("UINTPTR_MAX", scratch);
        }
    }

    chaz_ConfWriter_end_module();

    /* Integer literals. */

    chaz_ConfWriter_start_module("IntegerLiterals");

    if (has_stdint) {
        chaz_ConfWriter_add_sys_include("stdint.h");
    }
    else {
        /* We support only the following subset of stdint.h
         *   INT32_C
         *   INT64_C
         *   INTMAX_C
         *   UINT32_C
         *   UINT64_C
         *   UINTMAX_C
         */
        if (has_32) {
            if (strcmp(i32_t_postfix, "") == 0) {
                chaz_ConfWriter_add_global_def("INT32_C(n)", "n");
            }
            else {
                sprintf(scratch, "n##%s", i32_t_postfix);
                chaz_ConfWriter_add_global_def("INT32_C(n)", scratch);
            }
            sprintf(scratch, "n##%s", u32_t_postfix);
            chaz_ConfWriter_add_global_def("UINT32_C(n)", scratch);
        }
        if (has_64) {
            sprintf(scratch, "n##%s", i64_t_postfix);
            chaz_ConfWriter_add_global_def("INT64_C(n)", scratch);
            sprintf(scratch, "n##%s", u64_t_postfix);
            chaz_ConfWriter_add_global_def("UINT64_C(n)", scratch);
        }

        if (has_64) {
            sprintf(scratch, "n##%s", i64_t_postfix);
            chaz_ConfWriter_add_global_def("INTMAX_C(n)", scratch);
            sprintf(scratch, "n##%s", u64_t_postfix);
            chaz_ConfWriter_add_global_def("UINTMAX_C(n)", scratch);
        }
        else if (has_32) {
            if (strcmp(i32_t_postfix, "") == 0) {
                chaz_ConfWriter_add_global_def("INTMAX_C(n)", "n");
            }
            else {
                sprintf(scratch, "n##%s", i32_t_postfix);
                chaz_ConfWriter_add_global_def("INTMAX_C(n)", scratch);
            }
            sprintf(scratch, "n##%s", u32_t_postfix);
            chaz_ConfWriter_add_global_def("UINTMAX_C(n)", scratch);
        }
    }

    chaz_ConfWriter_end_module();

    /* Integer format strings. */

    chaz_ConfWriter_start_module("IntegerFormatStrings");

    if (has_inttypes) {
        if (chaz_CC_is_mingw()) {
            /* Suppress warnings about undefined inline function `llabs`
             * under MinGW.
             */
            chaz_ConfWriter_add_sys_include("stdlib.h");
        }
        chaz_ConfWriter_add_sys_include("inttypes.h");
    }

    {
        /* We support only the following subset of inttypes.h
         *   PRId32
         *   PRIi32
         *   PRIo32
         *   PRIu32
         *   PRIx32
         *   PRIX32
         *   PRId64
         *   PRIi64
         *   PRIo64
         *   PRIu64
         *   PRIx64
         *   PRIX64
         *   PRIdMAX
         *   PRIiMAX
         *   PRIoMAX
         *   PRIuMAX
         *   PRIxMAX
         *   PRIXMAX
         *   PRIdPTR
         *   PRIiPTR
         *   PRIoPTR
         *   PRIuPTR
         *   PRIxPTR
         *   PRIXPTR
         */
        const char *ptr;
        char macro_name_32[]  = "PRI.32";
        char macro_name_64[]  = "PRI.64";
        char macro_name_max[] = "PRI.MAX";
        char macro_name_ptr[] = "PRI.PTR";

        for (ptr = "diouxX"; ptr[0] != '\0'; ptr++) {
            int c = ptr[0];

            if (has_32) {
                sprintf(scratch, "\"%s%c\"", printf_modifier_32, c);

                if (!has_inttypes) {
                    macro_name_32[3] = c;
                    chaz_ConfWriter_add_global_def(macro_name_32, scratch);
                    if (!has_64) {
                        macro_name_max[3] = c;
                        chaz_ConfWriter_add_global_def(macro_name_max,
                                                       scratch);
                    }
                }
                if (!has_intptr_t && sizeof_ptr == 4) {
                    macro_name_ptr[3] = c;
                    chaz_ConfWriter_add_global_def(macro_name_ptr, scratch);
                }
            }
            if (has_64) {
                sprintf(scratch, "\"%s%c\"", printf_modifier_64, c);

                if (!has_inttypes) {
                    macro_name_64[3] = c;
                    chaz_ConfWriter_add_global_def(macro_name_64, scratch);
                    macro_name_max[3] = c;
                    chaz_ConfWriter_add_global_def(macro_name_max, scratch);
                }
                if (!has_intptr_t && sizeof_ptr == 8) {
                    macro_name_ptr[3] = c;
                    chaz_ConfWriter_add_global_def(macro_name_ptr, scratch);
                }
            }
        }
    }

    chaz_ConfWriter_end_module();
}

static int
chaz_Integers_machine_is_big_endian(void) {
    long one = 1;
    return !(*((char*)(&one)));
}



/***************************************************************************/

#line 17 "src/Charmonizer/Probe/LargeFiles.c"
/* #include "Charmonizer/Core/HeaderChecker.h" */
/* #include "Charmonizer/Core/Compiler.h" */
/* #include "Charmonizer/Core/ConfWriter.h" */
/* #include "Charmonizer/Core/Util.h" */
/* #include "Charmonizer/Probe/LargeFiles.h" */
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>

/* Module vars. */
static struct {
    char off64_type[10];
} chaz_LargeFiles = { "" };

/* Sets of symbols which might provide large file support for stdio. */
typedef struct chaz_LargeFiles_stdio64_combo {
    const char *includes;
    const char *fopen_command;
    const char *ftell_command;
    const char *fseek_command;
} chaz_LargeFiles_stdio64_combo;

/* Sets of symbols which might provide large file support for unbuffered i/o.
 */
typedef struct chaz_LargeFiles_unbuff_combo {
    const char *includes;
    const char *lseek_command;
    const char *pread64_command;
} chaz_LargeFiles_unbuff_combo;

/* Check for a 64-bit file pointer type.
 */
static int
chaz_LargeFiles_probe_off64(void);

/* Check what name 64-bit ftell, fseek go by.
 */
static void
chaz_LargeFiles_probe_stdio64(void);
static int
chaz_LargeFiles_try_stdio64(chaz_LargeFiles_stdio64_combo *combo);

/* Probe for 64-bit unbuffered i/o.
 */
static void
chaz_LargeFiles_probe_unbuff(void);

/* Check for a 64-bit lseek.
 */
static int
chaz_LargeFiles_probe_lseek(chaz_LargeFiles_unbuff_combo *combo);

/* Check for a 64-bit pread.
 */
static int
chaz_LargeFiles_probe_pread64(chaz_LargeFiles_unbuff_combo *combo);

void
chaz_LargeFiles_run(void) {
    int found_off64_t = false;
    const char *stat_includes = "#include <stdio.h>\n#include <sys/stat.h>";

    chaz_ConfWriter_start_module("LargeFiles");

    /* Find off64_t or equivalent. */
    found_off64_t = chaz_LargeFiles_probe_off64();
    if (found_off64_t) {
        chaz_ConfWriter_add_def("HAS_64BIT_OFFSET_TYPE", NULL);
        chaz_ConfWriter_add_def("off64_t",  chaz_LargeFiles.off64_type);
    }

    /* See if stdio variants with 64-bit support exist. */
    chaz_LargeFiles_probe_stdio64();

    /* Probe for 64-bit versions of lseek and pread (if we have an off64_t). */
    if (found_off64_t) {
        chaz_LargeFiles_probe_unbuff();
    }

    /* Make checks needed for testing. */
    if (chaz_HeadCheck_check_header("sys/stat.h")) {
        chaz_ConfWriter_append_conf("#define CHAZ_HAS_SYS_STAT_H\n");
    }
    if (chaz_HeadCheck_check_header("io.h")) {
        chaz_ConfWriter_append_conf("#define CHAZ_HAS_IO_H\n");
    }
    if (chaz_HeadCheck_check_header("fcntl.h")) {
        chaz_ConfWriter_append_conf("#define CHAZ_HAS_FCNTL_H\n");
    }
    if (chaz_HeadCheck_contains_member("struct stat", "st_size", stat_includes)) {
        chaz_ConfWriter_append_conf("#define CHAZ_HAS_STAT_ST_SIZE\n");
    }
    if (chaz_HeadCheck_contains_member("struct stat", "st_blocks", stat_includes)) {
        chaz_ConfWriter_append_conf("#define CHAZ_HAS_STAT_ST_BLOCKS\n");
    }

    chaz_ConfWriter_end_module();
}

static int
chaz_LargeFiles_probe_off64(void) {
    static const char off64_code[] =
        CHAZ_QUOTE(  %s                                        )
        CHAZ_QUOTE(  #include <stdio.h>                        )
        CHAZ_QUOTE(  int main()                                )
        CHAZ_QUOTE(  {                                         )
        CHAZ_QUOTE(      printf("%%d", (int)sizeof(%s));       )
        CHAZ_QUOTE(      return 0;                             )
        CHAZ_QUOTE(  }                                         );
    char code_buf[sizeof(off64_code) + 100];
    int i;
    int success = false;
    static const char* off64_options[] = {
        "off64_t",
        "off_t",
        "__int64",
        "long"
    };
    int num_off64_options = sizeof(off64_options) / sizeof(off64_options[0]);

    for (i = 0; i < num_off64_options; i++) {
        const char *candidate = off64_options[i];
        char *output;
        size_t output_len;
        int has_sys_types_h = chaz_HeadCheck_check_header("sys/types.h");
        const char *sys_types_include = has_sys_types_h
                                        ? "#include <sys/types.h>"
                                        : "";

        /* Execute the probe. */
        sprintf(code_buf, off64_code, sys_types_include, candidate);
        output = chaz_CC_capture_output(code_buf, &output_len);
        if (output != NULL) {
            long sizeof_candidate = strtol(output, NULL, 10);
            free(output);
            if (sizeof_candidate == 8) {
                strcpy(chaz_LargeFiles.off64_type, candidate);
                success = true;
                break;
            }
        }
    }
    return success;
}

static int
chaz_LargeFiles_try_stdio64(chaz_LargeFiles_stdio64_combo *combo) {
    static const char stdio64_code[] =
        CHAZ_QUOTE(  %s                                         )
        CHAZ_QUOTE(  #include <stdio.h>                         )
        CHAZ_QUOTE(  int main() {                               )
        CHAZ_QUOTE(      %s pos;                                )
        CHAZ_QUOTE(      FILE *f;                               )
        CHAZ_QUOTE(      f = %s("_charm_stdio64", "w");         )
        CHAZ_QUOTE(      if (f == NULL) return -1;              )
        CHAZ_QUOTE(      printf("%%d", (int)sizeof(%s));        )
        CHAZ_QUOTE(      pos = %s(stdout);                      )
        CHAZ_QUOTE(      %s(stdout, 0, SEEK_SET);               )
        CHAZ_QUOTE(      return 0;                              )
        CHAZ_QUOTE(  }                                          );
    char *output = NULL;
    size_t output_len;
    char code_buf[sizeof(stdio64_code) + 200];
    int success = false;

    /* Prepare the source code. */
    sprintf(code_buf, stdio64_code, combo->includes,
            chaz_LargeFiles.off64_type, combo->fopen_command,
            chaz_LargeFiles.off64_type, combo->ftell_command,
            combo->fseek_command);

    /* Verify compilation and that the offset type has 8 bytes. */
    output = chaz_CC_capture_output(code_buf, &output_len);
    if (output != NULL) {
        long size = strtol(output, NULL, 10);
        if (size == 8) {
            success = true;
        }
        free(output);
    }

    if (!chaz_Util_remove_and_verify("_charm_stdio64")) {
        chaz_Util_die("Failed to remove '_charm_stdio64'");
    }

    return success;
}

static void
chaz_LargeFiles_probe_stdio64(void) {
    int i;
    static chaz_LargeFiles_stdio64_combo stdio64_combos[] = {
        { "#include <sys/types.h>\n", "fopen64",   "ftello64",  "fseeko64"  },
        { "#include <sys/types.h>\n", "fopen",     "ftello64",  "fseeko64"  },
        { "#include <sys/types.h>\n", "fopen",     "ftello",    "fseeko"    },
        { "",                         "fopen",     "ftell",     "fseek"     },
        { "",                         "fopen",     "_ftelli64", "_fseeki64" },
        { "",                         "fopen",     "ftell",     "fseek"     },
        { NULL, NULL, NULL, NULL }
    };

    for (i = 0; stdio64_combos[i].includes != NULL; i++) {
        chaz_LargeFiles_stdio64_combo combo = stdio64_combos[i];
        if (chaz_LargeFiles_try_stdio64(&combo)) {
            chaz_ConfWriter_add_def("HAS_64BIT_STDIO", NULL);
            chaz_ConfWriter_add_def("fopen64",  combo.fopen_command);
            chaz_ConfWriter_add_def("ftello64", combo.ftell_command);
            chaz_ConfWriter_add_def("fseeko64", combo.fseek_command);
            break;
        }
    }
}

static int
chaz_LargeFiles_probe_lseek(chaz_LargeFiles_unbuff_combo *combo) {
    static const char lseek_code[] =
        CHAZ_QUOTE( %s                                                       )
        CHAZ_QUOTE( #include <stdio.h>                                       )
        CHAZ_QUOTE( int main() {                                             )
        CHAZ_QUOTE(     int fd;                                              )
        CHAZ_QUOTE(     fd = open("_charm_lseek", O_WRONLY | O_CREAT, 0666); )
        CHAZ_QUOTE(     if (fd == -1) { return -1; }                         )
        CHAZ_QUOTE(     %s(fd, 0, SEEK_SET);                                 )
        CHAZ_QUOTE(     printf("%%d", 1);                                    )
        CHAZ_QUOTE(     if (close(fd)) { return -1; }                        )
        CHAZ_QUOTE(     return 0;                                            )
        CHAZ_QUOTE( }                                                        );
    char code_buf[sizeof(lseek_code) + 100];
    char *output = NULL;
    size_t output_len;
    int success = false;

    /* Verify compilation. */
    sprintf(code_buf, lseek_code, combo->includes, combo->lseek_command);
    output = chaz_CC_capture_output(code_buf, &output_len);
    if (output != NULL) {
        success = true;
        free(output);
    }

    if (!chaz_Util_remove_and_verify("_charm_lseek")) {
        chaz_Util_die("Failed to remove '_charm_lseek'");
    }

    return success;
}

static int
chaz_LargeFiles_probe_pread64(chaz_LargeFiles_unbuff_combo *combo) {
    /* Code for checking 64-bit pread.  The pread call will fail, but that's
     * fine as long as it compiles. */
    static const char pread64_code[] =
        CHAZ_QUOTE(  %s                                     )
        CHAZ_QUOTE(  #include <stdio.h>                     )
        CHAZ_QUOTE(  int main() {                           )
        CHAZ_QUOTE(      int fd = 20;                       )
        CHAZ_QUOTE(      char buf[1];                       )
        CHAZ_QUOTE(      printf("1");                       )
        CHAZ_QUOTE(      %s(fd, buf, 1, 1);                 )
        CHAZ_QUOTE(      return 0;                          )
        CHAZ_QUOTE(  }                                      );
    char code_buf[sizeof(pread64_code) + 100];
    char *output = NULL;
    size_t output_len;
    int success = false;

    /* Verify compilation. */
    sprintf(code_buf, pread64_code, combo->includes, combo->pread64_command);
    output = chaz_CC_capture_output(code_buf, &output_len);
    if (output != NULL) {
        success = true;
        free(output);
    }

    return success;
}

static void
chaz_LargeFiles_probe_unbuff(void) {
    static chaz_LargeFiles_unbuff_combo unbuff_combos[] = {
        { "#include <unistd.h>\n#include <fcntl.h>\n", "lseek64",   "pread64" },
        { "#include <unistd.h>\n#include <fcntl.h>\n", "lseek",     "pread"      },
        { "#include <io.h>\n#include <fcntl.h>\n",     "_lseeki64", "NO_PREAD64" },
        { NULL, NULL, NULL }
    };
    int i;

    for (i = 0; unbuff_combos[i].lseek_command != NULL; i++) {
        chaz_LargeFiles_unbuff_combo combo = unbuff_combos[i];
        if (chaz_LargeFiles_probe_lseek(&combo)) {
            chaz_ConfWriter_add_def("HAS_64BIT_LSEEK", NULL);
            chaz_ConfWriter_add_def("lseek64", combo.lseek_command);
            break;
        }
    }
    for (i = 0; unbuff_combos[i].pread64_command != NULL; i++) {
        chaz_LargeFiles_unbuff_combo combo = unbuff_combos[i];
        if (chaz_LargeFiles_probe_pread64(&combo)) {
            chaz_ConfWriter_add_def("HAS_64BIT_PREAD", NULL);
            chaz_ConfWriter_add_def("pread64", combo.pread64_command);
            break;
        }
    }
}


/***************************************************************************/

#line 17 "src/Charmonizer/Probe/Memory.c"
/* #include "Charmonizer/Probe/Memory.h" */
/* #include "Charmonizer/Core/Compiler.h" */
/* #include "Charmonizer/Core/HeaderChecker.h" */
/* #include "Charmonizer/Core/ConfWriter.h" */
/* #include "Charmonizer/Core/Util.h" */
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

/* Probe for alloca() or equivalent. */
static void
chaz_Memory_probe_alloca(void);

void
chaz_Memory_run(void) {
    chaz_ConfWriter_start_module("Memory");

    chaz_Memory_probe_alloca();

    chaz_ConfWriter_end_module();
}

static void
chaz_Memory_probe_alloca(void) {
    static const char alloca_code[] =
        "#include <%s>\n"
        CHAZ_QUOTE(  int main() {                   )
        CHAZ_QUOTE(      void *foo = %s(1);         )
        CHAZ_QUOTE(      return 0;                  )
        CHAZ_QUOTE(  }                              );
    chaz_CFlags *temp_cflags = chaz_CC_get_temp_cflags();
    int has_alloca = false;
    char code_buf[sizeof(alloca_code) + 100];

    {
        /* OpenBSD needs sys/types.h for sys/mman.h to work and mmap() to be
         * available. Everybody else that has sys/mman.h should have
         * sys/types.h as well. */
        const char *mman_headers[] = {
            "sys/types.h",
            "sys/mman.h",
            NULL
        };
        if (chaz_HeadCheck_check_many_headers((const char**)mman_headers)) {
            chaz_ConfWriter_add_def("HAS_SYS_MMAN_H", NULL);
        }
    }

    /* Under GCC, alloca is a builtin that works without including the
     * correct header, generating only a warning. To avoid misdetection,
     * disable the alloca builtin temporarily. */
    if (chaz_CC_gcc_version_num()) {
        chaz_CFlags_append(temp_cflags, "-fno-builtin-alloca");
    }

    /* Unixen. */
    sprintf(code_buf, alloca_code, "alloca.h", "alloca");
    if (chaz_CC_test_link(code_buf)) {
        has_alloca = true;
        chaz_ConfWriter_add_def("HAS_ALLOCA_H", NULL);
        chaz_ConfWriter_add_def("alloca", "alloca");
    }
    if (!has_alloca) {
        sprintf(code_buf, alloca_code, "stdlib.h", "alloca");
        if (chaz_CC_test_link(code_buf)) {
            has_alloca = true;
            chaz_ConfWriter_add_def("ALLOCA_IN_STDLIB_H", NULL);
            chaz_ConfWriter_add_def("alloca", "alloca");
        }
    }

    /* Windows. */
    if (!has_alloca) {
        sprintf(code_buf, alloca_code, "malloc.h", "alloca");
        if (chaz_CC_test_link(code_buf)) {
            has_alloca = true;
            chaz_ConfWriter_add_def("HAS_MALLOC_H", NULL);
            chaz_ConfWriter_add_def("alloca", "alloca");
        }
    }
    if (!has_alloca) {
        sprintf(code_buf, alloca_code, "malloc.h", "_alloca");
        if (chaz_CC_test_link(code_buf)) {
            chaz_ConfWriter_add_def("HAS_MALLOC_H", NULL);
            chaz_ConfWriter_add_def("alloca", "_alloca");
        }
    }

    chaz_CFlags_clear(temp_cflags);
}



/***************************************************************************/

#line 17 "src/Charmonizer/Probe/RegularExpressions.c"
/* #include "Charmonizer/Core/HeaderChecker.h" */
/* #include "Charmonizer/Core/Compiler.h" */
/* #include "Charmonizer/Core/ConfWriter.h" */
/* #include "Charmonizer/Probe/RegularExpressions.h" */

void
chaz_RegularExpressions_run(void) {
    int has_regex_h     = chaz_HeadCheck_check_header("regex.h");
    int has_pcre_h      = chaz_HeadCheck_check_header("pcre.h");
    int has_pcreposix_h = chaz_HeadCheck_check_header("pcreposix.h");

    chaz_ConfWriter_start_module("RegularExpressions");

    /* PCRE headers. */
    if (has_pcre_h) {
        chaz_ConfWriter_add_def("HAS_PCRE_H", NULL);
    }
    if (has_pcreposix_h) {
        chaz_ConfWriter_add_def("HAS_PCREPOSIX_H", NULL);
    }

    /* Check for OS X enhanced regexes. */
    if (has_regex_h) {
        const char *reg_enhanced_code =
            CHAZ_QUOTE(  #include <regex.h>                             )
            CHAZ_QUOTE(  int main(int argc, char **argv) {              )
            CHAZ_QUOTE(      regex_t re;                                )
            CHAZ_QUOTE(      if (regcomp(&re, "^", REG_ENHANCED)) {     )
            CHAZ_QUOTE(          return 1;                              )
            CHAZ_QUOTE(      }                                          )
            CHAZ_QUOTE(      return 0;                                  )
            CHAZ_QUOTE(  }                                              );

        if (chaz_CC_test_compile(reg_enhanced_code)) {
            chaz_ConfWriter_add_def("HAS_REG_ENHANCED", NULL);
        }
    }

    chaz_ConfWriter_end_module();
}



/***************************************************************************/

#line 17 "src/Charmonizer/Probe/Strings.c"
/* #include "Charmonizer/Core/Compiler.h" */
/* #include "Charmonizer/Core/ConfWriter.h" */
/* #include "Charmonizer/Probe/Strings.h" */

#include <stdlib.h>

/* Check for C99-compatible snprintf and possible replacements.
 */
static void
chaz_Strings_probe_c99_snprintf(void);

void
chaz_Strings_run(void) {
    chaz_ConfWriter_start_module("Strings");

    /* Check for C99 snprintf. */
    chaz_Strings_probe_c99_snprintf();

    chaz_ConfWriter_end_module();
}

static void
chaz_Strings_probe_c99_snprintf(void) {
    static const char snprintf_code[] =
        CHAZ_QUOTE(  #include <stdio.h>                             )
        CHAZ_QUOTE(  int main() {                                   )
        CHAZ_QUOTE(      char buf[4];                               )
        CHAZ_QUOTE(      int  result;                               )
        CHAZ_QUOTE(      result = snprintf(buf, 4, "%s", "12345");  )
        CHAZ_QUOTE(      printf("%d", result);                      )
        CHAZ_QUOTE(      return 0;                                  )
        CHAZ_QUOTE(  }                                              );
    static const char detect__scprintf_code[] =
        CHAZ_QUOTE(  #include <stdio.h>                             )
        CHAZ_QUOTE(  int main() {                                   )
        CHAZ_QUOTE(      int  result;                               )
        CHAZ_QUOTE(      result = _scprintf("%s", "12345");         )
        CHAZ_QUOTE(      printf("%d", result);                      )
        CHAZ_QUOTE(      return 0;                                  )
        CHAZ_QUOTE(  }                                              );
    static const char detect__snprintf_code[] =
        CHAZ_QUOTE(  #include <stdio.h>                             )
        CHAZ_QUOTE(  int main() {                                   )
        CHAZ_QUOTE(      char buf[6];                               )
        CHAZ_QUOTE(      int  result;                               )
        CHAZ_QUOTE(      result = _snprintf(buf, 6, "%s", "12345"); )
        CHAZ_QUOTE(      printf("%d", result);                      )
        CHAZ_QUOTE(      return 0;                                  )
        CHAZ_QUOTE(  }                                              );
    char   *output = NULL;
    size_t  output_len;

    /* If the buffer passed to snprintf is too small, verify that snprintf
     * returns the length of the untruncated string which would have been
     * written to a large enough buffer.
     */
    output = chaz_CC_capture_output(snprintf_code, &output_len);
    if (output != NULL) {
        long result = strtol(output, NULL, 10);
        if (result == 5) {
            chaz_ConfWriter_add_def("HAS_C99_SNPRINTF", NULL);
        }
        free(output);
    }

    /* Test for _scprintf and _snprintf found in the MSVCRT.
     */
    output = chaz_CC_capture_output(detect__scprintf_code, &output_len);
    if (output != NULL) {
        chaz_ConfWriter_add_def("HAS__SCPRINTF", NULL);
        free(output);
    }
    output = chaz_CC_capture_output(detect__snprintf_code, &output_len);
    if (output != NULL) {
        chaz_ConfWriter_add_def("HAS__SNPRINTF", NULL);
        free(output);
    }
}


/***************************************************************************/

#line 17 "src/Charmonizer/Probe/SymbolVisibility.c"
/* #include "Charmonizer/Probe/SymbolVisibility.h" */
/* #include "Charmonizer/Core/Compiler.h" */
/* #include "Charmonizer/Core/ConfWriter.h" */
/* #include "Charmonizer/Core/Util.h" */
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

static const char chaz_SymbolVisibility_symbol_exporting_code[] =
    CHAZ_QUOTE(  %s int exported_function(void) {   )
    CHAZ_QUOTE(      return 42;                     )
    CHAZ_QUOTE(  }                                  )
    CHAZ_QUOTE(  int main(void) {                   )
    CHAZ_QUOTE(      return 0;                      )
    CHAZ_QUOTE(  }                                  );

void
chaz_SymbolVisibility_run(void) {
    chaz_CFlags *temp_cflags = chaz_CC_get_temp_cflags();
    int can_control_visibility = false;
    char code_buf[sizeof(chaz_SymbolVisibility_symbol_exporting_code) + 100];

    chaz_ConfWriter_start_module("SymbolVisibility");
    chaz_CFlags_set_warnings_as_errors(temp_cflags);

    /* Sun C. */
    if (!can_control_visibility) {
        char export_sun[] = "__global";
        sprintf(code_buf, chaz_SymbolVisibility_symbol_exporting_code,
                export_sun);
        if (chaz_CC_test_compile(code_buf)) {
            can_control_visibility = true;
            chaz_ConfWriter_add_def("EXPORT", export_sun);
            chaz_ConfWriter_add_def("IMPORT", export_sun);
        }
    }

    /* Windows. */
    if (!can_control_visibility) {
        char export_win[] = "__declspec(dllexport)";
        sprintf(code_buf, chaz_SymbolVisibility_symbol_exporting_code,
                export_win);
        if (chaz_CC_test_compile(code_buf)) {
            can_control_visibility = true;
            chaz_ConfWriter_add_def("EXPORT", export_win);
            if (chaz_CC_gcc_version_num()) {
                /*
                 * Under MinGW, symbols with dllimport storage class aren't
                 * constant. If a global variable is initialized to such a
                 * symbol, an "initializer element is not constant" error
                 * results. Omitting dllimport works, but has a small
                 * performance penalty.
                 */
                chaz_ConfWriter_add_def("IMPORT", NULL);
            }
            else {
                chaz_ConfWriter_add_def("IMPORT", "__declspec(dllimport)");
            }
        }
    }

    /* GCC. */
    if (!can_control_visibility) {
        char export_gcc[] = "__attribute__ ((visibility (\"default\")))";
        sprintf(code_buf, chaz_SymbolVisibility_symbol_exporting_code,
                export_gcc);
        if (chaz_CC_test_compile(code_buf)) {
            can_control_visibility = true;
            chaz_ConfWriter_add_def("EXPORT", export_gcc);
            chaz_ConfWriter_add_def("IMPORT", NULL);
        }
    }

    chaz_CFlags_clear(temp_cflags);

    /* Default. */
    if (!can_control_visibility) {
        chaz_ConfWriter_add_def("EXPORT", NULL);
        chaz_ConfWriter_add_def("IMPORT", NULL);
    }

    chaz_ConfWriter_end_module();
}



/***************************************************************************/

#line 17 "src/Charmonizer/Probe/UnusedVars.c"
/* #include "Charmonizer/Core/ConfWriter.h" */
/* #include "Charmonizer/Core/Util.h" */
/* #include "Charmonizer/Probe/UnusedVars.h" */
#include <string.h>
#include <stdio.h>


void
chaz_UnusedVars_run(void) {
    chaz_ConfWriter_start_module("UnusedVars");

    /* Write the macros (no test, these are the same everywhere). */
    chaz_ConfWriter_add_def("UNUSED_VAR(x)", "((void)x)");
    chaz_ConfWriter_add_def("UNREACHABLE_RETURN(type)", "return (type)0");

    chaz_ConfWriter_end_module();
}




/***************************************************************************/

#line 17 "src/Charmonizer/Probe/VariadicMacros.c"
/* #include "Charmonizer/Core/Compiler.h" */
/* #include "Charmonizer/Core/ConfWriter.h" */
/* #include "Charmonizer/Core/Util.h" */
/* #include "Charmonizer/Probe/VariadicMacros.h" */
#include <string.h>
#include <stdio.h>
#include <stdlib.h>


/* Code for verifying ISO-style variadic macros. */
static const char chaz_VariadicMacros_iso_code[] =
    CHAZ_QUOTE(  #include <stdio.h>                                    )
    CHAZ_QUOTE(  #define ISO_TEST(fmt, ...) \\                         )
    "                printf(fmt, __VA_ARGS__)                        \n"
    CHAZ_QUOTE(  int main() {                                          )
    CHAZ_QUOTE(      ISO_TEST("%d %d", 1, 1);                          )
    CHAZ_QUOTE(      return 0;                                         )
    CHAZ_QUOTE(  }                                                     );

/* Code for verifying GNU-style variadic macros. */
static const char chaz_VariadicMacros_gnuc_code[] =
    CHAZ_QUOTE(  #include <stdio.h>                                    )
    CHAZ_QUOTE(  #define GNU_TEST(fmt, args...) printf(fmt, ##args)    )
    CHAZ_QUOTE(  int main() {                                          )
    CHAZ_QUOTE(      GNU_TEST("%d %d", 1, 1);                          )
    CHAZ_QUOTE(      return 0;                                         )
    CHAZ_QUOTE(  }                                                     );

void
chaz_VariadicMacros_run(void) {
    char *output;
    size_t output_len;
    int has_varmacros = false;

    chaz_ConfWriter_start_module("VariadicMacros");

    /* Test for ISO-style variadic macros. */
    output = chaz_CC_capture_output(chaz_VariadicMacros_iso_code, &output_len);
    if (output != NULL) {
        has_varmacros = true;
        chaz_ConfWriter_add_def("HAS_VARIADIC_MACROS", NULL);
        chaz_ConfWriter_add_def("HAS_ISO_VARIADIC_MACROS", NULL);
        free(output);
    }

    /* Test for GNU-style variadic macros. */
    output = chaz_CC_capture_output(chaz_VariadicMacros_gnuc_code, &output_len);
    if (output != NULL) {
        if (has_varmacros == false) {
            has_varmacros = true;
            chaz_ConfWriter_add_def("HAS_VARIADIC_MACROS", NULL);
        }
        chaz_ConfWriter_add_def("HAS_GNUC_VARIADIC_MACROS", NULL);
        free(output);
    }

    chaz_ConfWriter_end_module();
}




#line 1 "runtime/common/charmonizer.main"
/* Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* Source fragment for the Clownfish runtime's charmonizer.c.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* #include "Charmonizer/Probe.h" */
/* #include "Charmonizer/Probe/AtomicOps.h" */
/* #include "Charmonizer/Probe/BuildEnv.h" */
/* #include "Charmonizer/Probe/DirManip.h" */
/* #include "Charmonizer/Probe/Floats.h" */
/* #include "Charmonizer/Probe/FuncMacro.h" */
/* #include "Charmonizer/Probe/Headers.h" */
/* #include "Charmonizer/Probe/Integers.h" */
/* #include "Charmonizer/Probe/LargeFiles.h" */
/* #include "Charmonizer/Probe/Memory.h" */
/* #include "Charmonizer/Probe/SymbolVisibility.h" */
/* #include "Charmonizer/Probe/VariadicMacros.h" */
/* #include "Charmonizer/Core/HeaderChecker.h" */
/* #include "Charmonizer/Core/CLI.h" */
/* #include "Charmonizer/Core/ConfWriter.h" */
/* #include "Charmonizer/Core/ConfWriterC.h" */
/* #include "Charmonizer/Core/ConfWriterPerl.h" */
/* #include "Charmonizer/Core/ConfWriterRuby.h" */

typedef struct cfish_MakeFile {
    chaz_MakeFile   *makefile;
    chaz_MakeBinary *lib;
    chaz_MakeBinary *test_lib;
    chaz_MakeVar    *cfh_var;
    chaz_CLI        *cli;

    /* Directories and files. */
    const char  *base_dir;
    const char  *host_src_dir;
    char        *core_dir;
    char        *test_dir;
    char        *autogen_src_dir;
    char        *autogen_inc_dir;
    char        *autogen_target;
} cfish_MakeFile;

static const char cfish_version[]       = "0.6.4";
static const char cfish_major_version[] = "0.6";

static void
S_add_compiler_flags(struct chaz_CLI *cli);

static chaz_CFlags*
S_link_flags(chaz_CLI *cli);

static cfish_MakeFile*
cfish_MakeFile_new(chaz_CLI *cli);

static void
cfish_MakeFile_destroy(cfish_MakeFile *self);

static void
cfish_MakeFile_write(cfish_MakeFile *self, chaz_CFlags *extra_link_flags);

static void
cfish_MakeFile_write_c_cfc_rules(cfish_MakeFile *self);

static void
cfish_MakeFile_write_c_test_rules(cfish_MakeFile *self);

static void
S_cfh_file_callback(const char *dir, char *file, void *context);

static int
S_ends_with(const char *string, const char *postfix);

static int
S_need_libpthread(chaz_CLI *cli);

int main(int argc, const char **argv) {
    chaz_CFlags *link_flags;

    /* Initialize. */
    chaz_CLI *cli
        = chaz_CLI_new(argv[0], "charmonizer: Probe C build environment");
    chaz_CLI_register(cli, "host", "specify host binding language",
                      CHAZ_CLI_ARG_REQUIRED);
    chaz_CLI_register(cli, "disable-threads", "whether to disable threads",
                      CHAZ_CLI_NO_ARG);
    chaz_CLI_set_usage(cli, "Usage: charmonizer [OPTIONS] [-- [CFLAGS]]");
    if (!chaz_Probe_parse_cli_args(argc, argv, cli)) {
        chaz_Probe_die_usage();
    }
    if (!chaz_CLI_defined(cli, "host")) {
        chaz_CLI_set(cli, "host", "c");
    }
    chaz_Probe_init(cli);
    S_add_compiler_flags(cli);

    /* Employ integer features but don't define stdint types in charmony.h. */
    chaz_ConfWriter_append_conf(
        "#define CHY_EMPLOY_INTEGERLIMITS\n"
        "#define CHY_EMPLOY_INTEGERLITERALS\n"
        "#define CHY_EMPLOY_INTEGERFORMATSTRINGS\n\n"
    );

    /* Run probe modules. Booleans, DirManip and LargeFiles are only needed for
     * the Charmonizer tests.
     */
    chaz_BuildEnv_run();
    chaz_DirManip_run();
    chaz_Headers_run();
    chaz_AtomicOps_run();
    chaz_FuncMacro_run();
    chaz_Booleans_run();
    chaz_Integers_run();
    chaz_Floats_run();
    chaz_LargeFiles_run();
    chaz_Memory_run();
    chaz_VariadicMacros_run();

    /* Local definitions. */
    chaz_ConfWriter_start_module("LocalDefinitions");
    if (chaz_HeadCheck_defines_symbol("__sync_bool_compare_and_swap", "")) {
        chaz_ConfWriter_add_def("HAS___SYNC_BOOL_COMPARE_AND_SWAP", NULL);
    }
    link_flags = S_link_flags(cli);
    chaz_ConfWriter_add_def("EXTRA_LDFLAGS",
                            chaz_CFlags_get_string(link_flags));
    chaz_ConfWriter_end_module();

    /* Write custom postamble. */
    chaz_ConfWriter_append_conf(
        "#ifdef CHY_HAS_SYS_TYPES_H\n"
        "  #include <sys/types.h>\n"
        "#endif\n\n"
    );
    chaz_ConfWriter_append_conf(
        "#ifdef CHY_HAS_ALLOCA_H\n"
        "  #include <alloca.h>\n"
        "#elif defined(CHY_HAS_MALLOC_H)\n"
        "  #include <malloc.h>\n"
        "#elif defined(CHY_ALLOCA_IN_STDLIB_H)\n"
        "  #include <stdlib.h>\n"
        "#endif\n\n"
    );
    chaz_ConfWriter_append_conf(
        "#ifdef CHY_HAS_WINDOWS_H\n"
        "  /* Target Windows XP. */\n"
        "  #ifndef WINVER\n"
        "    #define WINVER 0x0500\n"
        "  #endif\n"
        "  #ifndef _WIN32_WINNT\n"
        "    #define _WIN32_WINNT 0x0500\n"
        "  #endif\n"
        "#endif\n\n"
    );

    if (chaz_CLI_defined(cli, "enable-makefile")) {
        cfish_MakeFile *mf = cfish_MakeFile_new(cli);
        cfish_MakeFile_write(mf, link_flags);
        cfish_MakeFile_destroy(mf);
    }

    /* Clean up. */
    chaz_CFlags_destroy(link_flags);
    chaz_CLI_destroy(cli);
    chaz_Probe_clean_up();

    return 0;
}

static void
S_add_compiler_flags(struct chaz_CLI *cli) {
    chaz_CFlags *extra_cflags = chaz_CC_get_extra_cflags();

    if (chaz_Probe_gcc_version_num()) {
        if (!chaz_CC_is_mingw()) {
            /* When using the MSVCRT, -pedantic complains about %I64 format
             * specifiers.
             */
            chaz_CFlags_append(extra_cflags, "-pedantic");
        }
        chaz_CFlags_append(extra_cflags, "-Wall -Wextra -Wno-variadic-macros");
        if (strcmp(chaz_CLI_strval(cli, "host"), "perl") == 0) {
            chaz_CFlags_append(extra_cflags, "-DPERL_GCC_PEDANTIC");
        }

        /* Only core source files require this -- not our headers and
         * autogenerated files. */
        chaz_CFlags_append(extra_cflags, "-std=gnu99 -D_GNU_SOURCE");

        if (chaz_CLI_defined(cli, "enable-coverage")) {
            /* Some code paths in the float/int comparison code aren't
             * triggered if excess precision is allowed. */
            chaz_CFlags_append(extra_cflags, "-ffloat-store");
        }
    }
    else if (chaz_Probe_msvc_version_num()) {
        if (chaz_Probe_msvc_version_num() < 1800) {
            /* Compile as C++ under MSVC11 and below. */
            chaz_CFlags_append(extra_cflags, "/TP");
        }

        chaz_CFlags_append(extra_cflags, "/W3");
        /* Thwart stupid warnings. */
        chaz_CFlags_append(extra_cflags, "/D_CRT_SECURE_NO_WARNINGS");
        chaz_CFlags_append(extra_cflags, "/D_SCL_SECURE_NO_WARNINGS");

        if (chaz_Probe_msvc_version_num() < 1300) {
            /* Redefine 'for' to fix broken 'for' scoping under MSVC6. */
            chaz_CFlags_append(extra_cflags, "/Dfor=\"if(0);else for\"");
        }
    }

    chaz_CFlags_hide_symbols(extra_cflags);

    if (chaz_CLI_defined(cli, "disable-threads")) {
        chaz_CFlags_append(extra_cflags, "-DCFISH_NOTHREADS");
    }
}

static chaz_CFlags*
S_link_flags(chaz_CLI *cli) {
    chaz_CFlags *link_flags   = chaz_CC_new_cflags();
    const char  *math_library = chaz_Floats_math_library();

    if (math_library) {
        chaz_CFlags_add_external_lib(link_flags, math_library);
    }
    if (S_need_libpthread(cli)) {
        chaz_CFlags_add_external_lib(link_flags, "pthread");
    }
    if (chaz_CLI_defined(cli, "enable-coverage")) {
        chaz_CFlags_enable_code_coverage(link_flags);
    }

    return link_flags;
}

static cfish_MakeFile*
cfish_MakeFile_new(chaz_CLI *cli) {
    const char *dir_sep = chaz_OS_dir_sep();
    char *cfcore_filename = chaz_Util_join(dir_sep, "cfcore", "Clownfish.cfp",
                                           NULL);
    cfish_MakeFile *self = malloc(sizeof(cfish_MakeFile));

    self->makefile = chaz_MakeFile_new();
    self->lib      = NULL;
    self->test_lib = NULL;
    self->cfh_var  = NULL;
    self->cli      = cli;

    if (chaz_Util_can_open_file(cfcore_filename)) {
        self->base_dir = ".";
        self->core_dir = chaz_Util_strdup("cfcore");
        self->test_dir = chaz_Util_strdup("cftest");
    }
    else {
        self->base_dir = "..";
        self->core_dir = chaz_Util_join(dir_sep, "..", "core", NULL);
        self->test_dir = chaz_Util_join(dir_sep, "..", "test", NULL);
    }

    self->autogen_src_dir = chaz_Util_join(dir_sep, "autogen", "source", NULL);
    self->autogen_inc_dir
        = chaz_Util_join(dir_sep, "autogen", "include", NULL);
    self->autogen_target
        = chaz_Util_join(dir_sep, "autogen", "hierarchy.json", NULL);

    if (strcmp(chaz_CLI_strval(cli, "host"), "go") == 0) {
        /* TODO: Let Go bindings build code in "ext". */
        self->host_src_dir = "ext";
    }
    else if (chaz_CLI_defined(cli, "enable-python")) {
        /* TODO: Let Python bindings build code in "cfext". */
        self->host_src_dir = "cfext";
    }
    else if (strcmp(chaz_CLI_strval(cli, "host"), "c") == 0) {
        self->host_src_dir = "src";
    }
    else {
        self->host_src_dir = NULL;
    }

    free(cfcore_filename);
    return self;
}

static void
cfish_MakeFile_destroy(cfish_MakeFile *self) {
    chaz_MakeFile_destroy(self->makefile);

    free(self->core_dir);
    free(self->test_dir);
    free(self->autogen_inc_dir);
    free(self->autogen_src_dir);
    free(self->autogen_target);

    free(self);
}

static void
cfish_MakeFile_write(cfish_MakeFile *self, chaz_CFlags *extra_link_flags) {
    const char *dir_sep = chaz_OS_dir_sep();
    const char *host    = chaz_CLI_strval(self->cli, "host");

    const char *lib_objs      = NULL;
    const char *test_lib_objs = NULL;

    chaz_MakeVar  *var;
    chaz_MakeRule *rule;

    chaz_CFlags *extra_cflags = chaz_CC_get_extra_cflags();
    chaz_CFlags *makefile_cflags;
    chaz_CFlags *compile_flags;
    chaz_CFlags *link_flags;

    printf("Creating Makefile...\n");

    /* Directories */

    chaz_MakeFile_add_var(self->makefile, "BASE_DIR", self->base_dir);

    /* C compiler */

    chaz_MakeFile_add_var(self->makefile, "CC", chaz_CC_get_cc());

    makefile_cflags = chaz_CC_new_cflags();

    chaz_CFlags_enable_optimization(makefile_cflags);
    chaz_CFlags_enable_debugging(makefile_cflags);
    chaz_CFlags_disable_strict_aliasing(makefile_cflags);
    if (chaz_CLI_defined(self->cli, "enable-coverage")) {
        chaz_CFlags_enable_code_coverage(makefile_cflags);
    }

    chaz_CFlags_add_include_dir(makefile_cflags, ".");
    chaz_CFlags_add_include_dir(makefile_cflags, self->core_dir);
    if (self->host_src_dir) {
        chaz_CFlags_add_include_dir(makefile_cflags, self->host_src_dir);
    }
    chaz_CFlags_add_include_dir(makefile_cflags, self->autogen_inc_dir);

    var = chaz_MakeFile_add_var(self->makefile, "CFLAGS", NULL);
    chaz_MakeVar_append(var, chaz_CFlags_get_string(extra_cflags));
    chaz_MakeVar_append(var, chaz_CFlags_get_string(makefile_cflags));
    chaz_MakeVar_append(var, chaz_CC_get_cflags());

    chaz_CFlags_destroy(makefile_cflags);

    /* Core library. */

    if (strcmp(host, "c") == 0 || strcmp(host, "perl") == 0) {
        /* Shared library for C and Perl. */

        chaz_MakeFile_add_rule(self->makefile, "all",
                               "$(CLOWNFISH_SHARED_LIB)");

        self->lib
            = chaz_MakeFile_add_shared_lib(self->makefile, NULL, "clownfish",
                                           cfish_version, cfish_major_version);
        lib_objs = "$(CLOWNFISH_SHARED_LIB_OBJS)";

        compile_flags = chaz_MakeBinary_get_compile_flags(self->lib);
        chaz_CFlags_add_define(compile_flags, "CFP_CFISH", NULL);

        link_flags = chaz_MakeBinary_get_link_flags(self->lib);
        chaz_CFlags_enable_debugging(link_flags);
        chaz_CFlags_append(link_flags,
                           chaz_CFlags_get_string(extra_link_flags));
    }
    else {
        /* Static library for Go and Python. */

        chaz_MakeFile_add_rule(self->makefile, "static",
                               "$(CLOWNFISH_STATIC_LIB)"
                               " $(TESTCFISH_STATIC_LIB)");

        self->lib
            = chaz_MakeFile_add_static_lib(self->makefile, NULL, "clownfish");
        lib_objs = "$(CLOWNFISH_STATIC_LIB_OBJS)";

        if (strcmp(host, "python") == 0) {
            /* For Python, the static library is linked into a shared
             * library.
             */
            compile_flags = chaz_MakeBinary_get_compile_flags(self->lib);
            if (chaz_CC_msvc_version_num()) {
                /* Python uses /MT. */
                chaz_CFlags_append(compile_flags, "/MT");
            }
            else {
                chaz_CFlags_compile_shared_library(compile_flags);
            }
            chaz_CFlags_add_define(compile_flags, "CFP_CFISH", NULL);
            /* Test code isn't separated yet. */
            chaz_CFlags_add_define(compile_flags, "CFP_TESTCFISH", NULL);
        }
    }

    if (self->host_src_dir) {
        chaz_MakeBinary_add_src_dir(self->lib, self->host_src_dir);
    }
    chaz_MakeBinary_add_src_dir(self->lib, self->core_dir);
    chaz_MakeBinary_add_src_file(self->lib, self->autogen_src_dir,
                                 "cfish_parcel.c");

    /* Test library. */

    if (strcmp(host, "c") == 0 || strcmp(host, "perl") == 0) {
        /* Shared library for C and Perl. */

        self->test_lib
            = chaz_MakeFile_add_shared_lib(self->makefile, NULL, "testcfish",
                                           cfish_version, cfish_major_version);
        test_lib_objs = "$(TESTCFISH_SHARED_LIB_OBJS)";

        compile_flags = chaz_MakeBinary_get_compile_flags(self->test_lib);
        chaz_CFlags_add_define(compile_flags, "CFP_TESTCFISH", NULL);

        link_flags = chaz_MakeBinary_get_link_flags(self->test_lib);
        chaz_CFlags_enable_debugging(link_flags);
        chaz_CFlags_append(link_flags,
                           chaz_CFlags_get_string(extra_link_flags));
        chaz_CFlags_add_shared_lib(link_flags, NULL, "clownfish",
                                   cfish_major_version);

        chaz_MakeBinary_add_prereq(self->test_lib, "$(CLOWNFISH_SHARED_LIB)");
    }
    else {
        /* Static library for Go and Python. */

        self->test_lib
            = chaz_MakeFile_add_static_lib(self->makefile, NULL, "testcfish");
        test_lib_objs = "$(TESTCFISH_STATIC_LIB_OBJS)";

        if (strcmp(host, "python") == 0) {
            /* For Python, the static library is linked into a shared
             * library.
             */
            compile_flags = chaz_MakeBinary_get_compile_flags(self->test_lib);
            if (chaz_CC_msvc_version_num()) {
                /* Python uses /MT. */
                chaz_CFlags_append(compile_flags, "/MT");
            }
            else {
                chaz_CFlags_compile_shared_library(compile_flags);
            }
            chaz_CFlags_add_define(compile_flags, "CFP_TESTCFISH", NULL);
            /* Test code isn't separated yet. */
            chaz_CFlags_add_define(compile_flags, "CFP_CFISH", NULL);
        }
    }

    chaz_MakeBinary_add_src_dir(self->test_lib, self->test_dir);
    chaz_MakeBinary_add_src_file(self->test_lib, self->autogen_src_dir,
                                 "testcfish_parcel.c");

    /* Additional rules. */

    /* Object files depend on autogenerated headers. */
    chaz_MakeFile_add_rule(self->makefile, lib_objs, self->autogen_target);
    chaz_MakeFile_add_rule(self->makefile, test_lib_objs,
                           self->autogen_target);

    if (strcmp(host, "c") == 0) {
        cfish_MakeFile_write_c_cfc_rules(self);
        cfish_MakeFile_write_c_test_rules(self);
    }

    /* Targets to compile object files for Perl. */
    if (strcmp(host, "perl") == 0) {
        char *objects;

        chaz_MakeFile_add_rule(self->makefile, "core_objects",
                               "$(CLOWNFISH_SHARED_LIB_OBJS)");
        objects = chaz_MakeBinary_obj_string(self->lib);
        chaz_ConfWriter_add_def("CORE_OBJECTS", objects);
        free(objects);

        chaz_MakeFile_add_rule(self->makefile, "test_objects",
                               "$(TESTCFISH_SHARED_LIB_OBJS)");
        objects = chaz_MakeBinary_obj_string(self->test_lib);
        chaz_ConfWriter_add_def("TEST_OBJECTS", objects);
        free(objects);
    }

    chaz_MakeFile_write(self->makefile);
}

static void
cfish_MakeFile_write_c_cfc_rules(cfish_MakeFile *self) {
    static const char *const autogen_src_files[] = {
        "cfish_parcel.c",
        "testcfish_parcel.c",
        NULL
    };

    chaz_MakeRule *rule;

    const char *dir_sep  = chaz_OS_dir_sep();
    const char *exe_ext  = chaz_CC_exe_ext();

    char *cfc_dir;
    char *cfc_exe;
    char *cfc_command;

    int i;

    cfc_dir = chaz_Util_join(dir_sep, self->base_dir, "..", "compiler", "c",
                             NULL);
    cfc_exe = chaz_Util_join("", cfc_dir, dir_sep, "cfc", exe_ext, NULL);

    rule = chaz_MakeFile_add_rule(self->makefile, cfc_exe, NULL);
    chaz_MakeRule_add_make_command(rule, cfc_dir, NULL);

    self->cfh_var = chaz_MakeFile_add_var(self->makefile, "CLOWNFISH_HEADERS",
                                          NULL);
    chaz_Make_list_files(self->core_dir, "cfh", S_cfh_file_callback, self);
    chaz_Make_list_files(self->test_dir, "cfh", S_cfh_file_callback, self);

    rule = chaz_MakeFile_add_rule(self->makefile, self->autogen_target,
                                  cfc_exe);
    chaz_MakeRule_add_prereq(rule, "$(CLOWNFISH_HEADERS)");
    cfc_command = chaz_Util_join("", cfc_exe, " --source=", self->core_dir,
                                 " --source=", self->test_dir,
                                 " --dest=autogen --header=cfc_header", NULL);
    chaz_MakeRule_add_command(rule, cfc_command);

    /* Tell make how autogenerated source files are built. */
    for (i = 0; autogen_src_files[i] != NULL; ++i) {
        char *path = chaz_Util_join(dir_sep, self->autogen_src_dir,
                                    autogen_src_files[i], NULL);
        chaz_MakeFile_add_rule(self->makefile, path, self->autogen_target);
        free(path);
    }

    rule = chaz_MakeFile_clean_rule(self->makefile);
    chaz_MakeRule_add_recursive_rm_command(rule, "autogen");
    chaz_MakeRule_add_make_command(rule, cfc_dir, "clean");

    rule = chaz_MakeFile_distclean_rule(self->makefile);
    chaz_MakeRule_add_make_command(rule, cfc_dir, "distclean");

    free(cfc_dir);
    free(cfc_exe);
    free(cfc_command);
}

static void
cfish_MakeFile_write_c_test_rules(cfish_MakeFile *self) {
    chaz_MakeBinary *exe;
    chaz_CFlags     *link_flags;
    chaz_MakeRule   *rule;

    exe = chaz_MakeFile_add_exe(self->makefile, "t", "test_cfish");
    chaz_MakeBinary_add_src_file(exe, "t", "test_cfish.c");

    link_flags = chaz_MakeBinary_get_link_flags(exe);
    chaz_CFlags_add_rpath(link_flags, "\"$$PWD\"");
    chaz_CFlags_add_shared_lib(link_flags, NULL, "testcfish",
                               cfish_major_version);
    chaz_CFlags_add_shared_lib(link_flags, NULL, "clownfish",
                               cfish_major_version);

    chaz_MakeBinary_add_prereq(exe, "$(TESTCFISH_SHARED_LIB)");
    chaz_MakeBinary_add_prereq(exe, "$(CLOWNFISH_SHARED_LIB)");

    chaz_MakeFile_add_rule(self->makefile, "$(TEST_CFISH_EXE_OBJS)",
                           self->autogen_target);

    rule = chaz_MakeFile_add_rule(self->makefile, "test", "$(TEST_CFISH_EXE)");
    chaz_MakeRule_add_command(rule, "$(TEST_CFISH_EXE)");

    if (chaz_OS_shell_type() == CHAZ_OS_POSIX) {
        const char *valgrind_command;

        rule = chaz_MakeFile_add_rule(self->makefile, "valgrind",
                                      "$(TEST_CFISH_EXE)");
        if (chaz_CC_binary_format() == CHAZ_CC_BINFMT_ELF) {
            valgrind_command = "LD_LIBRARY_PATH=. CLOWNFISH_VALGRIND=1"
                               " valgrind"
                               " --leak-check=full"
                               " $(TEST_CFISH_EXE)";
        }
        else {
            valgrind_command = "CLOWNFISH_VALGRIND=1"
                               " valgrind"
                               " --leak-check=full"
                               " $(TEST_CFISH_EXE)";
        }
        chaz_MakeRule_add_command(rule, valgrind_command);
    }

    if (chaz_CLI_defined(self->cli, "enable-coverage")) {
        rule = chaz_MakeFile_add_rule(self->makefile, "coverage",
                                      "$(TEST_CFISH_EXE)");
        chaz_MakeRule_add_command(rule,
                                  "lcov"
                                  " --zerocounters"
                                  " --directory $(BASE_DIR)");
        chaz_MakeRule_add_command(rule, "$(TEST_CFISH_EXE)");
        chaz_MakeRule_add_command(rule,
                                  "lcov"
                                  " --capture"
                                  " --directory $(BASE_DIR)"
                                  " --base-directory ."
                                  " --rc lcov_branch_coverage=1"
                                  " --output-file clownfish.info");
        chaz_MakeRule_add_command(rule,
                                  "lcov"
                                  " --remove clownfish.info"
                                  " '/usr/include/*'"
                                  " 'c/autogen/*'"
                                  " 'core/Clownfish/Test.*'"
                                  " 'core/Clownfish/Test/*'"
                                  " 'core/TestClownfish.*'"
                                  " --rc lcov_branch_coverage=1"
                                  " --output-file clownfish.info");
        chaz_MakeRule_add_command(rule,
                                  "genhtml"
                                  " --branch-coverage"
                                  " --output-directory coverage"
                                  " clownfish.info");

        rule = chaz_MakeFile_clean_rule(self->makefile);
        chaz_MakeRule_add_rm_command(rule, "clownfish.info");
        chaz_MakeRule_add_recursive_rm_command(rule, "coverage");
    }
}

static void
S_cfh_file_callback(const char *dir, char *file, void *context) {
    cfish_MakeFile *self = (cfish_MakeFile*)context;
    const char *dir_sep = chaz_OS_dir_sep();
    char *cfh_file;

    if (!S_ends_with(file, ".cfh")) {
        chaz_Util_warn("Unexpected Clownfish header filename: %s", file);
        return;
    }

    cfh_file = chaz_Util_join(dir_sep, dir, file, NULL);
    chaz_MakeVar_append(self->cfh_var, cfh_file);
    free(cfh_file);
}

static int
S_ends_with(const char *string, const char *postfix) {
    size_t len         = strlen(string);
    size_t postfix_len = strlen(postfix);
    return len >= postfix_len
           && memcmp(string + len - postfix_len, postfix, postfix_len) == 0;
}

static int
S_need_libpthread(chaz_CLI *cli) {
    static const char source[] =
        "#include <pthread.h>\n"
        "\n"
        "int main() {\n"
        "    pthread_create(0, 0, 0, 0);\n"
        "    pthread_key_create(0, 0);\n"
        "    return 0;\n"
        "}\n";
    chaz_CFlags *temp_cflags;

    if (chaz_CLI_defined(cli, "disable-threads")
        || chaz_HeadCheck_check_header("windows.h")
    ) {
        return 0;
    }

    if (!chaz_HeadCheck_check_header("pthread.h")) {
        chaz_Util_die("pthread.h not found. Try --disable-threads.");
    }

    if (chaz_CC_test_link(source)) {
        return 0;
    }

    temp_cflags = chaz_CC_get_temp_cflags();
    chaz_CFlags_add_external_lib(temp_cflags, "pthread");
    if (!chaz_CC_test_link(source)) {
        chaz_Util_die("Can't link with libpthread. Try --disable-threads.");
    }
    chaz_CFlags_clear(temp_cflags);

    return 1;
}

