mirror of
https://github.com/reactos/RosBE.git
synced 2025-02-07 19:56:34 +00:00
![Colin Finck](/assets/img/avatar_default.png)
- Mix in parts of a patch by Brant Young, which itself is based on an old patch by Filip (see http://code.google.com/p/ccache-win32/wiki/WhyNativeWin32Ccache) - Change bInheritHandles for the CreateProcess call in failed() to TRUE, so that the compilation of the RC files works (they are preprocessed with gcc and piped to a file) - Add a safe version of mkstemp based on glibc sources, so that we don't run into race condition problems - Use CSIDL_LOCAL_APPDATA as the default base path for the ccache directory, not CSIDL_PROFILE svn path=/trunk/tools/RosBE/; revision=746
500 lines
8.9 KiB
C
500 lines
8.9 KiB
C
/*
|
|
Copyright (C) Andrew Tridgell 2002
|
|
|
|
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., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*/
|
|
|
|
#include "ccache.h"
|
|
|
|
static FILE *logfile;
|
|
|
|
/* log a message to the CCACHE_LOGFILE location */
|
|
void cc_log(const char *format, ...)
|
|
{
|
|
va_list ap;
|
|
extern char *cache_logfile;
|
|
|
|
if (!cache_logfile) return;
|
|
|
|
if (!logfile) logfile = fopen(cache_logfile, "a");
|
|
if (!logfile) return;
|
|
|
|
va_start(ap, format);
|
|
vfprintf(logfile, format, ap);
|
|
va_end(ap);
|
|
fflush(logfile);
|
|
}
|
|
|
|
/* something went badly wrong! */
|
|
void fatal(const char *msg)
|
|
{
|
|
cc_log("FATAL: %s\n", msg);
|
|
exit(1);
|
|
}
|
|
|
|
/* copy all data from one file descriptor to another */
|
|
void copy_fd(int fd_in, int fd_out)
|
|
{
|
|
char buf[10240];
|
|
int n;
|
|
|
|
while ((n = read(fd_in, buf, sizeof(buf))) > 0) {
|
|
if (write(fd_out, buf, n) != n) {
|
|
fatal("Failed to copy fd");
|
|
}
|
|
}
|
|
}
|
|
|
|
/* copy a file - used when hard links don't work
|
|
the copy is done via a temporary file and atomic rename
|
|
*/
|
|
int copy_file(const char *src, const char *dest)
|
|
{
|
|
int fd1, fd2;
|
|
char buf[10240];
|
|
int n;
|
|
char *tmp_name;
|
|
|
|
#ifndef _WIN32
|
|
mode_t mask;
|
|
#endif
|
|
|
|
x_asprintf(&tmp_name, "%s.XXXXXX", dest);
|
|
|
|
fd1 = open(src, O_RDONLY|O_BINARY);
|
|
if (fd1 == -1) {
|
|
free(tmp_name);
|
|
return -1;
|
|
}
|
|
|
|
fd2 = mkstemp(tmp_name);
|
|
if (fd2 == -1) {
|
|
close(fd1);
|
|
free(tmp_name);
|
|
return -1;
|
|
}
|
|
|
|
while ((n = read(fd1, buf, sizeof(buf))) > 0) {
|
|
if (write(fd2, buf, n) != n) {
|
|
close(fd2);
|
|
close(fd1);
|
|
unlink(tmp_name);
|
|
free(tmp_name);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
close(fd1);
|
|
|
|
/* get perms right on the tmp file */
|
|
#ifndef _WIN32
|
|
mask = umask(0);
|
|
fchmod(fd2, 0666 & ~mask);
|
|
umask(mask);
|
|
#endif
|
|
|
|
/* the close can fail on NFS if out of space */
|
|
if (close(fd2) == -1) {
|
|
unlink(tmp_name);
|
|
free(tmp_name);
|
|
return -1;
|
|
}
|
|
|
|
unlink(dest);
|
|
|
|
if (rename(tmp_name, dest) == -1) {
|
|
unlink(tmp_name);
|
|
free(tmp_name);
|
|
return -1;
|
|
}
|
|
|
|
free(tmp_name);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* make sure a directory exists */
|
|
int create_dir(const char *dir)
|
|
{
|
|
struct stat st;
|
|
if (stat(dir, &st) == 0) {
|
|
if (S_ISDIR(st.st_mode)) {
|
|
return 0;
|
|
}
|
|
errno = ENOTDIR;
|
|
return 1;
|
|
}
|
|
if (mkdir(dir, 0777) != 0 && errno != EEXIST) {
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
this is like asprintf() but dies if the malloc fails
|
|
note that we use vsnprintf in a rather poor way to make this more portable
|
|
*/
|
|
void x_asprintf(char **ptr, const char *format, ...)
|
|
{
|
|
va_list ap;
|
|
|
|
*ptr = NULL;
|
|
va_start(ap, format);
|
|
vasprintf(ptr, format, ap);
|
|
va_end(ap);
|
|
|
|
if (!ptr) fatal("out of memory in x_asprintf");
|
|
}
|
|
|
|
/*
|
|
this is like strdup() but dies if the malloc fails and add quotes
|
|
around the argument if it contains spaces.
|
|
*/
|
|
char*
|
|
x_quote_strdup(const char* s)
|
|
{
|
|
/* Protect against args containing spaces in them - unicode-able ? */
|
|
if (strchr(s, ' ') != NULL) {
|
|
size_t len = strlen(s); /* at least 1 as it holds ' ' */
|
|
char *new_arg = x_malloc(len+2*1+1); /* Make room for quoting */
|
|
|
|
/* Quote */
|
|
new_arg[0] = '"';
|
|
memcpy(new_arg+1, s, len);
|
|
new_arg[len+1] = '"';
|
|
new_arg[len+2] = 0;
|
|
|
|
/* Done */
|
|
cc_log("Quoted %s\n", new_arg);
|
|
return new_arg;
|
|
}
|
|
else
|
|
return x_strdup(s);
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
this is like strdup() but dies if the malloc fails
|
|
*/
|
|
char *x_strdup(const char *s)
|
|
{
|
|
char *ret;
|
|
ret = strdup(s);
|
|
if (!ret) {
|
|
fatal("out of memory in strdup\n");
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
this is like malloc() but dies if the malloc fails
|
|
*/
|
|
void *x_malloc(size_t size)
|
|
{
|
|
void *ret;
|
|
ret = malloc(size);
|
|
if (!ret) {
|
|
fatal("out of memory in malloc\n");
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
this is like realloc() but dies if the malloc fails
|
|
*/
|
|
void *x_realloc(void *ptr, size_t size)
|
|
{
|
|
void *p2;
|
|
if (!ptr) return x_malloc(size);
|
|
p2 = malloc(size);
|
|
if (!p2) {
|
|
fatal("out of memory in x_realloc");
|
|
}
|
|
if (ptr) {
|
|
memcpy(p2, ptr, size);
|
|
free(ptr);
|
|
}
|
|
return p2;
|
|
}
|
|
|
|
|
|
/*
|
|
revsusive directory traversal - used for cleanup
|
|
fn() is called on all files/dirs in the tree
|
|
*/
|
|
void traverse(const char *dir, void (*fn)(const char *, struct stat *))
|
|
{
|
|
DIR *d;
|
|
struct dirent *de;
|
|
|
|
d = opendir(dir);
|
|
if (!d) return;
|
|
|
|
while ((de = readdir(d)) != 0) {
|
|
char *fname;
|
|
struct stat st;
|
|
|
|
if (strcmp(de->d_name,".") == 0) continue;
|
|
if (strcmp(de->d_name,"..") == 0) continue;
|
|
|
|
if (strlen(de->d_name) == 0) continue;
|
|
|
|
x_asprintf(&fname, "%s"PATH_SEP"%s", dir, de->d_name);
|
|
if (lstat(fname, &st)) {
|
|
if (errno != ENOENT) {
|
|
perror(fname);
|
|
}
|
|
free(fname);
|
|
continue;
|
|
}
|
|
|
|
if (S_ISDIR(st.st_mode)) {
|
|
traverse(fname, fn);
|
|
}
|
|
|
|
fn(fname, &st);
|
|
free(fname);
|
|
}
|
|
|
|
closedir(d);
|
|
}
|
|
|
|
|
|
/* return the base name of a file - caller frees */
|
|
char *str_basename(const char *s)
|
|
{
|
|
char *p = strrchr(s, PATH_SEP_CHAR);
|
|
if (p) {
|
|
return x_strdup(p+1);
|
|
}
|
|
|
|
return x_strdup(s);
|
|
}
|
|
|
|
/* return the dir name of a file - caller frees */
|
|
char *dirname(char *s)
|
|
{
|
|
char *p;
|
|
s = x_strdup(s);
|
|
p = strrchr(s, PATH_SEP_CHAR);
|
|
if (p) {
|
|
*p = 0;
|
|
}
|
|
return s;
|
|
}
|
|
|
|
int lock_fd(int fd)
|
|
{
|
|
#ifdef _WIN32
|
|
return _locking(fd, _LK_NBLCK, 1);
|
|
#else
|
|
struct flock fl;
|
|
int ret;
|
|
|
|
fl.l_type = F_WRLCK;
|
|
fl.l_whence = SEEK_SET;
|
|
fl.l_start = 0;
|
|
fl.l_len = 1;
|
|
fl.l_pid = 0;
|
|
|
|
/* not sure why we would be getting a signal here,
|
|
but one user claimed it is possible */
|
|
do {
|
|
ret = fcntl(fd, F_SETLKW, &fl);
|
|
} while (ret == -1 && errno == EINTR);
|
|
return ret;
|
|
#endif
|
|
}
|
|
|
|
|
|
/* return size on disk of a file */
|
|
size_t file_size(struct stat *st)
|
|
{
|
|
#ifdef _WIN32
|
|
return st->st_size;
|
|
#else
|
|
size_t size = st->st_blocks * 512;
|
|
if ((size_t)st->st_size > size) {
|
|
/* probably a broken stat() call ... */
|
|
size = (st->st_size + 1023) & ~1023;
|
|
}
|
|
return size;
|
|
#endif
|
|
}
|
|
|
|
|
|
/* a safe open/create for read-write */
|
|
int safe_open(const char *fname)
|
|
{
|
|
int fd = open(fname, O_RDWR|O_BINARY);
|
|
if (fd == -1 && errno == ENOENT) {
|
|
fd = open(fname, O_RDWR|O_CREAT|O_EXCL|O_BINARY, 0666);
|
|
if (fd == -1 && errno == EEXIST) {
|
|
fd = open(fname, O_RDWR|O_BINARY);
|
|
}
|
|
}
|
|
return fd;
|
|
}
|
|
|
|
/* display a kilobyte unsigned value in M, k or G */
|
|
void display_size(unsigned v)
|
|
{
|
|
if (v > 1024*1024) {
|
|
printf("%8.1f Gbytes", v/((double)(1024*1024)));
|
|
} else if (v > 1024) {
|
|
printf("%8.1f Mbytes", v/((double)(1024)));
|
|
} else {
|
|
printf("%8u Kbytes", v);
|
|
}
|
|
}
|
|
|
|
/* return a value in multiples of 1024 give a string that can end
|
|
in K, M or G
|
|
*/
|
|
size_t value_units(const char *s)
|
|
{
|
|
char m;
|
|
double v = atof(s);
|
|
m = s[strlen(s)-1];
|
|
switch (m) {
|
|
case 'G':
|
|
case 'g':
|
|
default:
|
|
v *= 1024*1024;
|
|
break;
|
|
case 'M':
|
|
case 'm':
|
|
v *= 1024;
|
|
break;
|
|
case 'K':
|
|
case 'k':
|
|
v *= 1;
|
|
break;
|
|
}
|
|
return (size_t)v;
|
|
}
|
|
|
|
|
|
/*
|
|
a sane realpath() function, trying to cope with stupid path limits and
|
|
a broken API
|
|
*/
|
|
#ifndef WIN32
|
|
char *x_realpath(const char *path)
|
|
{
|
|
int maxlen;
|
|
char *ret, *p;
|
|
#ifdef PATH_MAX
|
|
maxlen = PATH_MAX;
|
|
#elif defined(MAXPATHLEN)
|
|
maxlen = MAXPATHLEN;
|
|
#elif defined(_PC_PATH_MAX)
|
|
maxlen = pathconf(path, _PC_PATH_MAX);
|
|
#endif
|
|
if (maxlen < 4096) maxlen = 4096;
|
|
|
|
ret = x_malloc(maxlen);
|
|
|
|
#if HAVE_REALPATH
|
|
p = realpath(path, ret);
|
|
#else
|
|
/* yes, there are such systems. This replacement relies on
|
|
the fact that when we call x_realpath we only care about symlinks */
|
|
{
|
|
int len = readlink(path, ret, maxlen-1);
|
|
if (len == -1) {
|
|
free(ret);
|
|
return NULL;
|
|
}
|
|
ret[len] = 0;
|
|
p = ret;
|
|
}
|
|
#endif
|
|
if (p) {
|
|
p = x_strdup(p);
|
|
free(ret);
|
|
return p;
|
|
}
|
|
free(ret);
|
|
return NULL;
|
|
}
|
|
#endif
|
|
|
|
/* a getcwd that will returns an allocated buffer */
|
|
char *gnu_getcwd(void)
|
|
{
|
|
unsigned size = 128;
|
|
|
|
for(;;) {
|
|
char *buffer = (char *)x_malloc(size);
|
|
if (_getcwd(buffer, size) == buffer) {
|
|
return buffer;
|
|
}
|
|
free(buffer);
|
|
if (errno != ERANGE) {
|
|
return 0;
|
|
}
|
|
size *= 2;
|
|
}
|
|
}
|
|
|
|
/* create an empty file */
|
|
int create_empty_file(const char *fname)
|
|
{
|
|
int fd;
|
|
|
|
fd = open(fname, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL|O_BINARY, 0666);
|
|
if (fd == -1) {
|
|
return -1;
|
|
}
|
|
close(fd);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
return current users home directory or die
|
|
*/
|
|
const char *get_home_directory(void)
|
|
{
|
|
const char *p = NULL;
|
|
|
|
#ifdef _WIN32
|
|
static char szPath[MAX_PATH];
|
|
|
|
/* "Documents and Settings\user\Application Data" is CSIDL_LOCAL_APPDATA */
|
|
if(SHGetSpecialFolderPathA(NULL, szPath, CSIDL_LOCAL_APPDATA, FALSE))
|
|
{
|
|
return szPath;
|
|
}
|
|
#endif
|
|
p = getenv("HOME");
|
|
if (p) {
|
|
return p;
|
|
}
|
|
#ifdef HAVE_GETPWUID
|
|
{
|
|
struct passwd *pwd = getpwuid(getuid());
|
|
if (pwd) {
|
|
return pwd->pw_dir;
|
|
}
|
|
}
|
|
#endif
|
|
fatal("Unable to determine home directory");
|
|
return NULL;
|
|
}
|
|
|