diff options
Diffstat (limited to 'src/blogc-make/exec.c')
-rw-r--r-- | src/blogc-make/exec.c | 297 |
1 files changed, 297 insertions, 0 deletions
diff --git a/src/blogc-make/exec.c b/src/blogc-make/exec.c new file mode 100644 index 0000000..75b7c00 --- /dev/null +++ b/src/blogc-make/exec.c @@ -0,0 +1,297 @@ +/* + * blogc: A blog compiler. + * Copyright (C) 2015-2016 Rafael G. Martins <rafael@rafaelmartins.eng.br> + * + * This program can be distributed under the terms of the BSD License. + * See the file LICENSE. + */ + +#include <stdbool.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <errno.h> +#include <libgen.h> +#include "../common/error.h" +#include "../common/file.h" +#include "../common/utils.h" +#include "ctx.h" +#include "exec.h" +#include "settings.h" + + +int +bm_exec_command(const char *cmd, const char *input, char **output, + char **error, bc_error_t **err) +{ + if (err == NULL || *err != NULL) + return 3; + + int fd_in[2]; + if (-1 == pipe(fd_in)) { + *err = bc_error_new_printf(BLOGC_MAKE_ERROR_EXEC, + "Failed to create stdin pipe: %s", strerror(errno)); + return 3; + } + + int fd_out[2]; + if (-1 == pipe(fd_out)) { + *err = bc_error_new_printf(BLOGC_MAKE_ERROR_EXEC, + "Failed to create stdout pipe: %s", strerror(errno)); + close(fd_in[0]); + close(fd_in[1]); + return 3; + } + + int fd_err[2]; + if (-1 == pipe(fd_err)) { + *err = bc_error_new_printf(BLOGC_MAKE_ERROR_EXEC, + "Failed to create stderr pipe: %s", strerror(errno)); + close(fd_in[0]); + close(fd_in[1]); + close(fd_out[0]); + close(fd_out[1]); + return 3; + } + + pid_t pid = fork(); + if (pid == -1) { + *err = bc_error_new_printf(BLOGC_MAKE_ERROR_EXEC, + "Failed to fork: %s", strerror(errno)); + close(fd_in[0]); + close(fd_in[1]); + close(fd_out[0]); + close(fd_out[1]); + close(fd_err[0]); + close(fd_err[1]); + return 3; + } + + // child + if (pid == 0) { + close(fd_in[1]); + close(fd_out[0]); + close(fd_err[0]); + + dup2(fd_in[0], STDIN_FILENO); + dup2(fd_out[1], STDOUT_FILENO); + dup2(fd_err[1], STDERR_FILENO); + + char *const argv[] = { + "/bin/sh", + "-c", + (char*) cmd, + NULL, + }; + + execv(argv[0], argv); + + exit(1); + } + + // parent + close(fd_in[0]); + close(fd_out[1]); + close(fd_err[1]); + + if (input != NULL) { + if (-1 == write(fd_in[1], input, strlen(input))) { + *err = bc_error_new_printf(BLOGC_MAKE_ERROR_EXEC, + "Failed to write to stdin pipe: %s", strerror(errno)); + close(fd_in[1]); + close(fd_out[0]); + close(fd_err[0]); + return 3; + } + } + + close(fd_in[1]); + + char buffer[BC_FILE_CHUNK_SIZE]; + ssize_t s; + + bc_string_t *out = NULL; + while(0 != (s = read(fd_out[0], buffer, BC_FILE_CHUNK_SIZE))) { + if (s == -1) { + *err = bc_error_new_printf(BLOGC_MAKE_ERROR_EXEC, + "Failed to read from stdout pipe: %s", strerror(errno)); + close(fd_out[0]); + close(fd_err[0]); + bc_string_free(out, true); + return 3; + } + if (out == NULL) { + out = bc_string_new(); + } + bc_string_append_len(out, buffer, s); + } + if (out != NULL) { + *output = bc_string_free(out, false); + } + close(fd_out[0]); + + out = NULL; + while(0 != (s = read(fd_err[0], buffer, BC_FILE_CHUNK_SIZE))) { + if (s == -1) { + *err = bc_error_new_printf(BLOGC_MAKE_ERROR_EXEC, + "Failed to read from stderr pipe: %s", strerror(errno)); + close(fd_err[0]); + bc_string_free(out, true); + return 3; + } + if (out == NULL) + out = bc_string_new(); + bc_string_append_len(out, buffer, s); + } + if (out != NULL) { + *error = bc_string_free(out, false); + } + close(fd_err[0]); + + int status; + waitpid(pid, &status, 0); + + return WEXITSTATUS(status); +} + + +static void +list_variables(const char *key, const char *value, bc_string_t *str) +{ + char *tmp = bc_shell_quote(value); + bc_string_append_printf(str, " -D %s=%s", key, tmp); + free(tmp); +} + + +char* +bm_exec_build_blogc_cmd(bm_settings_t *settings, bc_trie_t *variables, + bool listing, const char *template, const char *output, bool sources_stdin) +{ + bc_string_t *rv = bc_string_new(); + + const char *locale = NULL; + if (settings != NULL) { + locale = bc_trie_lookup(settings->settings, "locale"); + } + if (locale != NULL) { + char *tmp = bc_shell_quote(locale); + bc_string_append_printf(rv, "LC_ALL=%s ", tmp); + free(tmp); + } + + bc_string_append(rv, "blogc"); + + if (settings != NULL) { + bc_trie_foreach(settings->env, + (bc_trie_foreach_func_t) list_variables, rv); + } + + bc_trie_foreach(variables, (bc_trie_foreach_func_t) list_variables, rv); + + if (listing) { + bc_string_append(rv, " -l"); + } + + if (template != NULL) { + char *tmp = bc_shell_quote(template); + bc_string_append_printf(rv, " -t %s", tmp); + free(tmp); + } + + if (output != NULL) { + char *tmp = bc_shell_quote(output); + bc_string_append_printf(rv, " -o %s", tmp); + free(tmp); + } + + if (sources_stdin) { + bc_string_append(rv, " -i"); + } + + return bc_string_free(rv, false); +} + + +int +bm_exec_blogc(bm_settings_t *settings, bc_trie_t *variables, bool listing, + bm_filectx_t *template, bm_filectx_t *output, bc_slist_t *sources, + bool verbose, bool only_first_source) +{ + bc_string_t *input = bc_string_new(); + for (bc_slist_t *l = sources; l != NULL; l = l->next) { + bc_string_append_printf(input, "%s\n", ((bm_filectx_t*) l->data)->path); + if (only_first_source) + break; + } + + char *cmd = bm_exec_build_blogc_cmd(settings, variables, listing, + template->path, output->path, input->len > 0); + + if (verbose) + printf("%s\n", cmd); + else + printf(" BLOGC %s\n", output->short_path); + fflush(stdout); + + char *out = NULL; + char *err = NULL; + bc_error_t *error = NULL; + + int rv = bm_exec_command(cmd, input->str, &out, &err, &error); + + if (error != NULL) { + bc_error_print(error, "blogc-make"); + free(cmd); + free(out); + free(err); + bc_string_free(input, true); + bc_error_free(error); + return 3; + } + + if (rv != 0) { + if (verbose) { + fprintf(stderr, + "error: Failed to execute command.\n" + "\n" + "STATUS CODE: %d\n", rv); + if (input->len > 0) { + fprintf(stderr, "\nSTDIN:\n" + "----------------------------->8-----------------------------\n" + "%s\n" + "----------------------------->8-----------------------------\n", + bc_str_strip(input->str)); + } + if (out != NULL) { + fprintf(stderr, "\nSTDOUT:\n" + "----------------------------->8-----------------------------\n" + "%s\n" + "----------------------------->8-----------------------------\n", + bc_str_strip(out)); + } + if (err != NULL) { + fprintf(stderr, "\nSTDERR:\n" + "----------------------------->8-----------------------------\n" + "%s\n" + "----------------------------->8-----------------------------\n", + bc_str_strip(err)); + } + } + else { + fprintf(stderr, + "error: Failed to execute command, returned status code: %d\n", + rv); + } + } + + bc_string_free(input, true); + free(cmd); + free(out); + free(err); + + return rv; +} |