See http://distcc.samba.org/faq.html#gdb This is ftp://ftp.gtk.org/pub/users/timj/patches/distcc-line3.diff rediffed against distcc-2.18.3, with one tiny bugfix; here's the bugfix (as a unified diff against postproc.c): +#if 0 strcpy (dirname + dl, pp_source_name); +#else + /* Original patch appended pp_source_name, but + * that was wrong if it contained a directory part; + * if dir was /foo and source_name was bar/bletch, + * it caused embedded paths of form /foo/bar/bar/blech.c + */ + dirname[dl] = 0; +#endif diff -Naur distcc-2.18.3/Makefile.in distcc-2.18.3-postproc/Makefile.in --- distcc-2.18.3/Makefile.in 2004-10-23 22:05:48.000000000 -0700 +++ distcc-2.18.3-postproc/Makefile.in 2005-12-10 13:09:08.000000000 -0800 @@ -165,6 +165,7 @@ src/rpc.o src/tempfile.o src/bulk.o src/help.o src/filename.o \ src/lock.o \ src/netutil.o \ + src/postproc.o \ src/pump.o \ src/sendfile.o \ src/safeguard.o src/snprintf.o src/timeval.o \ @@ -232,6 +233,7 @@ src/mon.c src/mon-notify.c src/mon-text.c \ src/mon-gnome.c \ src/ncpus.c src/netutil.c \ + src/postproc.c \ src/prefork.c src/pump.c \ src/remote.c src/renderer.c src/rpc.c \ src/safeguard.c src/sendfile.c src/setuid.c src/serve.c \ @@ -243,6 +245,7 @@ HEADERS = src/access.h \ + src/postproc.h \ src/bulk.h \ src/clinet.h src/compile.h \ src/daemon.h \ diff -Naur distcc-2.18.3/src/compile.c distcc-2.18.3-postproc/src/compile.c --- distcc-2.18.3/src/compile.c 2004-10-01 17:47:07.000000000 -0700 +++ distcc-2.18.3-postproc/src/compile.c 2005-12-10 13:09:08.000000000 -0800 @@ -44,6 +44,7 @@ #include "lock.h" #include "timeval.h" #include "compile.h" +#include "postproc.h" /** @@ -154,6 +155,8 @@ /* FIXME: argv_stripped is leaked. */ + if ((ret = dcc_post_process_set_source(input_fname)) != 0) + goto fallback; if ((ret = dcc_compile_remote(argv_stripped, input_fname, cpp_fname, diff -Naur distcc-2.18.3/src/postproc.c distcc-2.18.3-postproc/src/postproc.c --- distcc-2.18.3/src/postproc.c 1969-12-31 16:00:00.000000000 -0800 +++ distcc-2.18.3-postproc/src/postproc.c 2005-12-10 13:09:39.000000000 -0800 @@ -0,0 +1,375 @@ +/* -*- c-file-style: "java"; indent-tabs-mode: nil; fill-column: 78 -*- + * + * distcc -- A simple distributed compiler system + * $Header: $ + * + * Copyright (C) 2002 by Tim Janik + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "distcc.h" +#include "trace.h" +#include "assert.h" +#include "postproc.h" + + +typedef unsigned char uchar; + +/* buffered input */ +#define BUFFER_SIZE 4000 +typedef struct { + int fd; + uchar *bound; + uchar buffer[BUFFER_SIZE + 1]; + uchar *text; +} PPInput; + +static inline int peek_char (PPInput *inp) +{ + if (inp->text < inp->bound) + return *inp->text; + else if (inp->fd >= 0) { + int count; + uchar *buffer = inp->buffer; + do + count = read (inp->fd, buffer, BUFFER_SIZE); + while (count == -1 && (errno == EINTR || errno == EAGAIN)); + if (count < 1) { + inp->fd = -1; + return EOF; + } else { + inp->text = buffer; + inp->bound = buffer + count; + return *inp->text; + } + } else + return EOF; +} + +static inline int get_char (PPInput *inp) +{ + int c = peek_char (inp); + if (c != EOF) + inp->text++; + return c; +} + +static void unget_char (PPInput *inp, + uchar c) +{ + /* we only allow unget backs after a non-EOF get_char() */ + assert (inp->text > inp->buffer); + inp->text--; + inp->text[0] = c; +} + + +/* buffered output */ +typedef struct { + int fd; + uchar *p; + uchar buffer[BUFFER_SIZE + 1]; +} PPOutput; + +static void flush_output (PPOutput *out) +{ + int count, n = out->p - out->buffer; + errno = 0; + if (n) { + do + count = write (out->fd, out->buffer, n); + while (count == -1 && (errno == EINTR || errno == EAGAIN)); + if (count != n) + rs_log_error ("failed to write %u bytes: %s", n, strerror (errno)); + } + out->p = out->buffer; +} + +static inline void write_char (PPOutput *out, + int ch) +{ + *(out->p++) = ch; + if (out->p - out->buffer >= BUFFER_SIZE) + flush_output (out); +} + + +/* source file path name */ +static char *pp_source_name = NULL; + +/* rarely triggered. post processed output usually doesn't + * contain comments (an exception would be gcc -E -C) + */ +static void post_process_comment_rest (PPInput *inp, + PPOutput *out, + int term /* must be '\n' or '/' */) +{ + int seenast = FALSE; + /* read up to \n or * followed by / */ + while (1) { + int c = get_char (inp); + switch (c) { + case EOF: + return; /* broken input */ + case '*': + write_char (out, c); + seenast = TRUE; + break; + case '/': + write_char (out, c); + if (seenast && term == c) + return; + break; + case '\n': + write_char (out, c); + if (term == c) + return; + seenast = FALSE; + break; + default: + write_char (out, c); + seenast = FALSE; + break; + } + } +} + +/* process the rest of a string, after the initial '\'' or '\"' has been read */ +static inline void post_process_string_rest (PPInput *inp, + PPOutput *out, + int term /* must be '\"' or '\'' */) +{ + int escape = FALSE; + while (1) { + int c = get_char (inp); + switch (c) { + case EOF: + return; /* broken input */ + case '\\': + write_char (out, c); + escape = !escape; + break; + case '\"': + case '\'': + write_char (out, c); + if (c == term && !escape) + return; + escape = FALSE; + break; + default: + write_char (out, c); + escape = FALSE; + break; + } + } +} + +/* handle the case where a line starts with '#' which indicates + * preprocessor instructions. the ones to care about here are + * '# 3 "files/foo.c"', i.e. source file references. those are + * patched up to only contain absolute pathnames. + */ +static int post_process_hashcross (PPInput *inp, + PPOutput *out, + const char *pathprefix) +{ + int c; + /* check whether here comes a #-line directive, hash already parsed. + * we need to return the last char processed for our caller to update state. + */ + + /* read up spaces */ + c = get_char (inp); + while (c == ' ' || c == '\t') { + write_char (out, c); + c = get_char (inp); + } + /* read up digits */ + while (c >= '0' && c <= '9') { + write_char (out, c); + c = get_char (inp); + } + /* read up spaces */ + while (c == ' ' || c == '\t') { + write_char (out, c); + c = get_char (inp); + } + /* read up double quote */ + if (c != '\"') + goto abort; + write_char (out, c); + c = get_char (inp); + /* here it comes: if c is a slash, we got an absolute + * pathname. otherwise we insert our prefix and handle + * the rest as an ordinary string. + */ + if (c != '/') { + const char *p = pathprefix; + while (*p) + write_char (out, *p++); + } + unget_char (inp, c); + post_process_string_rest (inp, out, '\"'); + return '\"'; + + abort: + if (c != EOF) + write_char (out, c); + return c; +} + +static void post_process (PPInput *inp, + PPOutput *out, + const char *pathprefix) +{ + int seennewline = TRUE; + int lastc, c = '\n'; + while (1) { + lastc = c; + c = get_char (inp); + switch (c) { + case EOF: + return; + case '\n': + write_char (out, c); + seennewline = TRUE; + break; + case '#': + write_char (out, c); + if (seennewline) { + c = post_process_hashcross (inp, out, pathprefix); + if (c == EOF) + return; + } + seennewline = c == '\n'; + break; + case ' ': + case '\t': + write_char (out, c); + /* preserve seennewline */ + break; + case '*': + write_char (out, c); + if (lastc == '/') { + post_process_comment_rest (inp, out, '/'); + c = '/'; + } + seennewline = FALSE; + break; + case '\"': case '\'': + write_char (out, c); + post_process_string_rest (inp, out, c); + seennewline = FALSE; + break; + default: + write_char (out, c); + seennewline = FALSE; + break; + } + } +} + +int dcc_post_process (const char *in_fname, + char **out_fname) +{ + char *p, *dirname; + PPOutput out = { -1, 0, { 0, } }; + PPInput inp = { -1, 0, { 0, }, 0 }; + int ret; + + rs_trace("dcc_post_process(%s,)", in_fname); + if ((ret = dcc_make_tmpnam ("ppline", ".i", out_fname))) + return ret; + + inp.fd = open (in_fname, O_RDONLY); + if (inp.fd < 0) { + rs_log_error ("opening input \"%s\" failed: %s", in_fname, strerror (errno)); + return -1; + } + + out.p = out.buffer; + out.fd = open (*out_fname, O_CREAT | O_TRUNC | O_WRONLY, 0644); + if (out.fd < 0) { + rs_log_error ("opening output \"%s\" failed: %s", *out_fname, strerror (errno)); + close (inp.fd); + return -1; + } + + if (!pp_source_name) + pp_source_name = strdup (""); + + /* figure slash terminated directory name */ + if (pp_source_name[0] == '/') { + dirname = strdup (pp_source_name); + } else { + int dl, sl = strlen (pp_source_name); + char cdirbuf[8192]; + char *cdir = getcwd (cdirbuf, sizeof (cdirbuf)); + if (!cdir) + return -1; + dl = strlen (cdir); + dirname = malloc (dl + 1 + sl + 1); + strcpy (dirname, cdir); + dirname[dl++] = '/'; +#if 0 + strcpy (dirname + dl, pp_source_name); +#else + /* Original patch appended pp_source_name, but + * that was wrong if it contained a directory part; + * if dir was /foo and source_name was bar/bletch, + * it caused embedded paths of form /foo/bar/bar/blech.c + */ + dirname[dl] = 0; +#endif + } + p = strrchr (dirname, '/'); + if (p) + p[1] = 0; + rs_trace("dcc_post_process: pp_source_name %s, setting dirname to %s", pp_source_name, dirname); + + post_process (&inp, &out, dirname); + flush_output (&out); + free (dirname); + + close (inp.fd); + if (close (out.fd)) { + rs_log_error ("flushing output %s failed: %s\n", *out_fname, strerror (errno)); + return -1; + } + return 0; +} + +int dcc_post_process_set_source (const char *source_name) +{ + rs_trace("dcc_post_process_set_source(%s)", source_name ? source_name : ""); + if (pp_source_name) + free (pp_source_name); + pp_source_name = source_name ? strdup (source_name) : NULL; + return 0; +} diff -Naur distcc-2.18.3/src/postproc.h distcc-2.18.3-postproc/src/postproc.h --- distcc-2.18.3/src/postproc.h 1969-12-31 16:00:00.000000000 -0800 +++ distcc-2.18.3-postproc/src/postproc.h 2005-12-10 13:09:08.000000000 -0800 @@ -0,0 +1,28 @@ +/* -*- c-file-style: "java"; indent-tabs-mode: nil; fill-column: 78 -*- + * + * distcc -- A simple distributed compiler system + * $Header: $ + * + * Copyright (C) 2002 by Tim Janik + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ + +/* postproc.c */ +int dcc_post_process_set_source (const char *source_name); +int dcc_post_process (const char *in_fname, + char **out_fname); + diff -Naur distcc-2.18.3/src/remote.c distcc-2.18.3-postproc/src/remote.c --- distcc-2.18.3/src/remote.c 2004-10-23 22:05:49.000000000 -0700 +++ distcc-2.18.3-postproc/src/remote.c 2005-12-10 13:09:08.000000000 -0800 @@ -48,6 +48,7 @@ #include "lock.h" #include "compile.h" #include "bulk.h" +#include "postproc.h" /* @@ -183,6 +184,7 @@ int ssh_status; off_t doti_size; struct timeval before, after; + char *pp_fname; if (gettimeofday(&before, NULL)) rs_log_warning("gettimeofday failed"); @@ -208,7 +210,9 @@ if ((ret = dcc_wait_for_cpp(cpp_pid, status, input_fname))) goto out; - if ((ret = dcc_x_file(to_net_fd, cpp_fname, "DOTI", host->compr, &doti_size))) + if ((ret = dcc_post_process(cpp_fname, &pp_fname))) + goto out; + if ((ret = dcc_x_file(to_net_fd, pp_fname, "DOTI", host->compr, &doti_size))) goto out; rs_trace("client finished sending request to server");