#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <assert.h>
#include <sys/stat.h>
#include "distcc.h"
#include "trace.h"
#include "util.h"
#include "exitcode.h"
#include "snprintf.h"
#ifdef XCODE_INTEGRATION
# include "xci.h"
#endif
int dcc_argv_append(char **argv, char *toadd)
{
int l = dcc_argv_len(argv);
argv[l] = toadd;
argv[l+1] = NULL;
return 0;
}
static const char *dcc_optx_ext_lookup(const char *language_name) {
if (!strcmp(language_name, "c") ||
!strcmp(language_name, "cpp-output")) {
return ".i";
} else if (!strcmp(language_name, "c++") ||
!strcmp(language_name, "c++-cpp-output")) {
return ".ii";
} else if (!strcmp(language_name, "objective-c") ||
!strcmp(language_name, "objc-cpp-output")) {
return ".mi";
} else if (!strcmp(language_name, "objective-c++") ||
!strcmp(language_name, "objc++-cpp-output")) {
return ".mii";
} else {
return NULL;
}
}
static void dcc_note_compiled(const char *input_file, const char *output_file)
{
const char *input_base, *output_base;
input_base = dcc_find_basename(input_file);
output_base = dcc_find_basename(output_file);
rs_log(RS_LOG_INFO|RS_LOG_NONAME,
"compile from %s to %s", input_base, output_base);
}
int dcc_scan_args(char *argv[], char **input_file, char **output_file,
char ***ret_newargv, const char **forced_cpp_ext)
{
int seen_opt_c = 0, seen_opt_s = 0;
int i;
char *a, *optx_lang;
const char *optx_ext = NULL;
int ret;
if ((ret = dcc_copy_argv(argv, ret_newargv, 2)) != 0)
return ret;
argv = *ret_newargv;
dcc_trace_argv("scanning arguments", argv);
#ifdef XCODE_INTEGRATION
if (argv[0] && !strcmp(argv[0], "--host-info")) {
return 0;
}
#endif
if (argv[0] && argv[0][0] == '-') {
rs_log_error("unrecognized distcc option: %s", argv[0]);
exit(EXIT_BAD_ARGUMENTS);
}
*input_file = *output_file = NULL;
for (i = 0; (a = argv[i]); i++) {
if (a[0] == '-') {
if (!strcmp(a, "-E")) {
rs_trace("-E call for cpp must be local");
return EXIT_DISTCC_FAILED;
} else if (!strcmp(a, "-MD") || !strcmp(a, "-MMD")) {
} else if (!strcmp(a, "-MG") || !strcmp(a, "-MP")) {
} else if (!strcmp(a, "-MF") || !strcmp(a, "-MT") ||
!strcmp(a, "-MQ")) {
i++;
} else if (!strncmp(a, "-MF", 3) || !strncmp(a, "-MT", 3) ||
!strncmp(a, "-MQ", 3)) {
} else if (a[1] == 'M') {
rs_trace("%s implies -E (maybe) and must be local", a);
return EXIT_DISTCC_FAILED;
} else if (!strcmp(a, "-march=native")) {
rs_trace("-march=native generates code for local machine; "
"must be local");
return EXIT_DISTCC_FAILED;
} else if (!strcmp(a, "-mtune=native")) {
rs_trace("-mtune=native optimizes for local machine; "
"must be local");
return EXIT_DISTCC_FAILED;
} else if (str_startswith("-Wa,", a)) {
if (strstr(a, ",-a") || strstr(a, "--MD")) {
rs_trace("%s must be local", a);
return EXIT_DISTCC_FAILED;
}
} else if (str_startswith("-specs=", a)) {
rs_trace("%s must be local", a);
return EXIT_DISTCC_FAILED;
} else if (!strcmp(a, "-S")) {
seen_opt_s = 1;
} else if (!strcmp(a, "-fprofile-arcs")
|| !strcmp(a, "-ftest-coverage")) {
rs_log_info("compiler will emit profile info; must be local");
return EXIT_DISTCC_FAILED;
} else if (!strcmp(a, "-frepo")) {
rs_log_info("compiler will emit .rpo files; must be local");
return EXIT_DISTCC_FAILED;
} else if (!strcmp("-x", a)) {
optx_lang = argv[++i];
if (!optx_lang || !strlen(optx_lang)) {
rs_log_info("-x requires an argument; running locally");
return EXIT_DISTCC_FAILED;
}
if (*input_file) {
rs_log_info("-x must precede source file; running locally");
return EXIT_DISTCC_FAILED;
}
if (optx_ext) {
rs_log_info("at most one -x supported; running locally");
return EXIT_DISTCC_FAILED;
}
optx_ext = dcc_optx_ext_lookup(optx_lang);
if (!optx_ext) {
rs_log_info("unsupported -x language; running locally");
return EXIT_DISTCC_FAILED;
}
} else if (str_startswith("-x", a)) {
rs_log_info("-xlanguage unsupported, use -x language instead; "
"running locally");
return EXIT_DISTCC_FAILED;
} else if (str_startswith("-dr", a)) {
rs_log_info("gcc's debug option %s may write extra files; "
"running locally", a);
return EXIT_DISTCC_FAILED;
} else if (!strcmp(a, "-c")) {
seen_opt_c = 1;
} else if (!strcmp(a, "-o")) {
a = argv[++i];
goto GOT_OUTPUT;
} else if (str_startswith("-o", a)) {
a += 2;
goto GOT_OUTPUT;
}
} else {
if (dcc_is_source(a)) {
rs_trace("found input file \"%s\"", a);
if (*input_file) {
rs_log_info("do we have two inputs? i give up");
return EXIT_DISTCC_FAILED;
}
*input_file = a;
} else if (str_endswith(".o", a)) {
GOT_OUTPUT:
rs_trace("found object/output file \"%s\"", a);
if (*output_file) {
rs_log_info("called for link? i give up");
return EXIT_DISTCC_FAILED;
}
*output_file = a;
}
}
}
if (!seen_opt_c && !seen_opt_s) {
rs_log_info("compiler apparently called not for compile");
return EXIT_DISTCC_FAILED;
}
if (!*input_file) {
rs_log_info("no visible input file");
return EXIT_DISTCC_FAILED;
}
if (dcc_source_needs_local(*input_file))
return EXIT_DISTCC_FAILED;
if (!*output_file) {
char *ofile;
if (seen_opt_s) {
if (dcc_output_from_source(*input_file, ".s", &ofile))
return EXIT_DISTCC_FAILED;
} else if (seen_opt_c) {
if (dcc_output_from_source(*input_file, ".o", &ofile))
return EXIT_DISTCC_FAILED;
} else {
rs_log_crit("this can't be happening(%d)!", __LINE__);
return EXIT_DISTCC_FAILED;
}
rs_log_info("no visible output file, going to add \"-o %s\" at end",
ofile);
dcc_argv_append(argv, strdup("-o"));
dcc_argv_append(argv, ofile);
*output_file = ofile;
}
dcc_note_compiled(*input_file, *output_file);
if (strcmp(*output_file, "-") == 0) {
rs_log_info("output to stdout? running locally");
return EXIT_DISTCC_FAILED;
}
if (forced_cpp_ext)
*forced_cpp_ext = optx_ext;
return 0;
}
int dcc_set_action_opt(char **a, const char *new_c)
{
int gotone = 0;
for (; *a; a++)
if (!strcmp(*a, "-c") || !strcmp(*a, "-S")) {
*a = strdup(new_c);
if (*a == NULL) {
rs_log_error("strdup failed");
exit(EXIT_OUT_OF_MEMORY);
}
gotone = 1;
}
if (!gotone) {
rs_log_error("failed to find -c or -S");
return EXIT_DISTCC_FAILED;
} else {
return 0;
}
}
int dcc_set_output(char **a, char *ofname)
{
int i;
for (i = 0; a[i]; i++)
if (0 == strcmp(a[i], "-o") && a[i+1] != NULL) {
rs_trace("changed output from \"%s\" to \"%s\"", a[i+1], ofname);
free(a[i+1]);
a[i+1] = strdup(ofname);
if (a[i+1] == NULL) {
rs_log_crit("failed to allocate space for output parameter");
return EXIT_OUT_OF_MEMORY;
}
dcc_trace_argv("command after", a);
return 0;
} else if (0 == strncmp(a[i], "-o", 2)) {
char *newptr;
rs_trace("changed output from \"%s\" to \"%s\"", a[i]+2, ofname);
free(a[i]);
if (asprintf(&newptr, "-o%s", ofname) == -1) {
rs_log_crit("failed to allocate space for output parameter");
return EXIT_OUT_OF_MEMORY;
}
a[i] = newptr;
dcc_trace_argv("command after", a);
return 0;
}
rs_log_error("failed to find \"-o\"");
return EXIT_DISTCC_FAILED;
}
int dcc_set_input(char **a, char *ifname)
{
int i;
for (i =0; a[i]; i++)
if (dcc_is_source(a[i])) {
rs_trace("changed input from \"%s\" to \"%s\"", a[i], ifname);
free(a[i]);
a[i] = strdup(ifname);
if (a[i] == NULL) {
rs_log_crit("failed to allocate space for input parameter");
return EXIT_OUT_OF_MEMORY;
}
dcc_trace_argv("command after", a);
return 0;
}
rs_log_error("failed to find input file");
return EXIT_DISTCC_FAILED;
}
static int count_extra_args(char *dash_Wp_option) {
int extra_args = 0;
char *comma = dash_Wp_option + strlen("-Wp");
while (comma != NULL) {
char *opt = comma + 1;
comma = strchr(opt, ',');
if (str_startswith("-MD,", opt) ||
str_startswith("-MMD,", opt))
{
char *filename = comma + 1;
comma = strchr(filename, ',');
extra_args += 3;
} else {
extra_args++;
}
}
return extra_args;
}
static int copy_extra_args(char **dest_argv, char *dash_Wp_option,
int extra_args) {
int i = 0;
char *comma = dash_Wp_option + strlen("-Wp");
while (comma != NULL) {
char *opt = comma + 1;
comma = strchr(opt, ',');
if (comma) *comma = '\0';
dest_argv[i] = strdup(opt);
if (!dest_argv[i]) return EXIT_OUT_OF_MEMORY;
i++;
if (strcmp(opt, "-MD") == 0 || strcmp(opt, "-MMD") == 0) {
char *filename;
if (!comma) {
rs_log_warning("'-Wp,-MD' or '-Wp,-MMD' option is missing "
"filename argument");
break;
}
filename = comma + 1;
comma = strchr(filename, ',');
if (comma) *comma = '\0';
dest_argv[i] = strdup("-MF");
if (!dest_argv[i]) return EXIT_OUT_OF_MEMORY;
i++;
dest_argv[i] = strdup(filename);
if (!dest_argv[i]) return EXIT_OUT_OF_MEMORY;
i++;
}
}
assert(i == extra_args);
return 0;
}
int dcc_expand_preprocessor_options(char ***argv_ptr) {
int i, j, ret;
char **argv = *argv_ptr;
char **new_argv;
int argc = dcc_argv_len(argv);
for (i = 0; argv[i]; i++) {
if (str_startswith("-Wp,", argv[i])) {
int extra_args = count_extra_args(argv[i]);
assert(extra_args >= 1);
new_argv = calloc(argc + extra_args, sizeof(char *));
if (!new_argv) {
return EXIT_OUT_OF_MEMORY;
}
for (j = 0; j < i; j++) {
new_argv[j] = argv[j];
}
if ((ret = copy_extra_args(new_argv + i, argv[i],
extra_args)) != 0) {
free(new_argv);
return ret;
}
for (j = i + 1; j <= argc; j++) {
new_argv[j + extra_args - 1] = argv[j];
}
free(argv);
*argv_ptr = argv = new_argv;
}
}
return 0;
}
int dcc_xci_mask_developer_dir_in_argv(char **argv) {
if (!argv)
return EXIT_BAD_ARGUMENTS;
#ifdef XCODE_INTEGRATION
int i;
char *arg, *new_arg;
for (i = 0; (arg = argv[i]); i++) {
new_arg = dcc_xci_mask_developer_dir(arg);
if (new_arg) {
free(arg);
argv[i] = new_arg;
} else {
return EXIT_OUT_OF_MEMORY;
}
}
#endif
return 0;
}
int dcc_xci_unmask_developer_dir_in_argv(char **argv) {
if (!argv)
return EXIT_BAD_ARGUMENTS;
#ifdef XCODE_INTEGRATION
int i;
char *arg, *new_arg;
for (i = 0; (arg = argv[i]); i++) {
new_arg = dcc_xci_unmask_developer_dir(arg);
if (new_arg) {
free(arg);
argv[i] = new_arg;
} else {
return EXIT_OUT_OF_MEMORY;
}
}
#endif
return 0;
}