787 lines
20 KiB
C
787 lines
20 KiB
C
/*
|
|
* static char *rcsid_porting_c =
|
|
* "$Id: porting.c 11578 2009-02-23 22:02:27Z lalo $";
|
|
*/
|
|
|
|
/*
|
|
CrossFire, A Multiplayer game for X-windows
|
|
|
|
Copyright (C) 2006 Mark Wedel & Crossfire Development Team
|
|
Copyright (C) 1992 Frank Tore Johansen
|
|
|
|
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.
|
|
|
|
The authors can be reached via e-mail at crossfire-devel@real-time.com
|
|
*/
|
|
|
|
/**
|
|
* @file porting.c
|
|
* This file contains various functions that are not really unique for
|
|
* crossfire, but rather provides what should be standard functions
|
|
* for systems that do not have them. In this way, most of the
|
|
* nasty system dependent stuff is contained here, with the program
|
|
* calling these functions.
|
|
*/
|
|
|
|
#include <string.h>
|
|
#ifdef WIN32 /* ---win32 exclude/include headers */
|
|
#include "process.h"
|
|
#define pid_t int /* we include it non global, because there is a redefinition in python.h */
|
|
#else
|
|
#include <ctype.h>
|
|
#include <errno.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/wait.h>
|
|
|
|
#include <sys/param.h>
|
|
#include <stdio.h>
|
|
|
|
/* Need to pull in the HAVE_... values somehow */
|
|
/* win32 reminder: always put this in a ifndef win32 block */
|
|
#include <autoconf.h>
|
|
#endif
|
|
|
|
#ifdef HAVE_STDLIB_H
|
|
#include <stdlib.h>
|
|
#endif
|
|
|
|
#ifdef HAVE_UNISTD_H
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
#include <stdarg.h>
|
|
/* Has to be after above includes so we don't redefine some values */
|
|
#include "global.h"
|
|
|
|
/** Used to generate temporary unique name. */
|
|
static unsigned int curtmp = 0;
|
|
|
|
/*****************************************************************************
|
|
* File related functions
|
|
****************************************************************************/
|
|
|
|
/**
|
|
* A replacement for the tempnam() function since it's not defined
|
|
* at some unix variants. Do not use this function for new code, use
|
|
* tempnam_secure() instead.
|
|
*
|
|
* @param dir
|
|
* directory where to create the file. Can be NULL, in which case NULL is returned.
|
|
* @param pfx
|
|
* prefix to create unique name. Can be NULL.
|
|
* @return
|
|
* path to temporary file, or NULL if failure. Must be freed by caller.
|
|
*/
|
|
char *tempnam_local(const char *dir, const char *pfx) {
|
|
char *name;
|
|
pid_t pid = getpid();
|
|
|
|
/* HURD does not have a hard limit, but we do */
|
|
#ifndef MAXPATHLEN
|
|
#define MAXPATHLEN 4096
|
|
#endif
|
|
|
|
if (!pfx)
|
|
pfx = "cftmp.";
|
|
|
|
/* This is a pretty simple method - put the pid as a hex digit and
|
|
* just keep incrementing the last digit. Check to see if the file
|
|
* already exists - if so, we'll just keep looking - eventually we should
|
|
* find one that is free.
|
|
*/
|
|
if (dir != NULL) {
|
|
if (!(name = (char *)malloc(MAXPATHLEN)))
|
|
return(NULL);
|
|
do {
|
|
#ifdef HAVE_SNPRINTF
|
|
(void)snprintf(name, MAXPATHLEN, "%s/%s%hx.%u", dir, pfx, pid, curtmp);
|
|
#else
|
|
(void)sprintf(name, "%s/%s%hx%u", dir, pfx, pid, curtmp);
|
|
#endif
|
|
curtmp++;
|
|
} while (access(name, F_OK) != -1);
|
|
return(name);
|
|
}
|
|
return(NULL);
|
|
}
|
|
|
|
/**
|
|
* A replacement for the tempnam_local() function since that one is not very
|
|
* secure. This one will open the file in an atomic way on platforms where it is
|
|
* possible.
|
|
*
|
|
* @param dir
|
|
* Directory where to create the file. Can be NULL, in which case NULL is returned.
|
|
* @param pfx
|
|
* Prefix to create unique name. Can be NULL.
|
|
* @param filename
|
|
* This should be a pointer to a char*, the function will overwrite the char*
|
|
* with the name of the resulting file. Must be freed by caller. Value is
|
|
* unchanged if the function returns NULL.
|
|
* @return
|
|
* A pointer to a FILE opened exclusively, or NULL if failure.
|
|
* It is up to the caller to properly close it.
|
|
* @note
|
|
* The file will be opened read-write.
|
|
*
|
|
* @todo
|
|
* Maybe adding some #ifdef for non-UNIX? I don't have any such system around
|
|
* to test with.
|
|
*/
|
|
FILE *tempnam_secure(const char *dir, const char *pfx, char **filename) {
|
|
char *tempname = NULL;
|
|
int fd;
|
|
int i;
|
|
FILE *file = NULL;
|
|
#define MAXTMPRETRY 10
|
|
|
|
/* Limit number of retries to MAXRETRY */
|
|
for (i = 0; i < MAXTMPRETRY; i++) {
|
|
tempname = tempnam_local(dir, pfx);
|
|
/* tempnam_local only fails for really bad stuff, so lets bail out right
|
|
* away then.
|
|
*/
|
|
if (!tempname)
|
|
return NULL;
|
|
|
|
fd = open(tempname, O_CREAT|O_EXCL|O_RDWR, S_IRUSR|S_IWUSR);
|
|
if (fd != -1)
|
|
break;
|
|
if (errno == EEXIST)
|
|
LOG(llevError, "Created file detected in tempnam_secure. Someone hoping for a race condition?\n");
|
|
free(tempname);
|
|
}
|
|
/* Check that we successfully got an fd. */
|
|
if (fd == -1)
|
|
return NULL;
|
|
|
|
file = fdopen(fd, "w+");
|
|
if (!file) {
|
|
LOG(llevError, "fdopen() failed in tempnam_secure()!\n");
|
|
free(tempname);
|
|
return NULL;
|
|
}
|
|
|
|
*filename = tempname;
|
|
return file;
|
|
}
|
|
|
|
/**
|
|
* This function removes everything in the directory, and the directory itself.
|
|
*
|
|
* Errors are LOG() to error level.
|
|
*
|
|
* @param path
|
|
* directory to remove.
|
|
*
|
|
* @note
|
|
* will fail if any file has a name starting by .
|
|
*/
|
|
void remove_directory(const char *path) {
|
|
DIR *dirp;
|
|
char buf[MAX_BUF];
|
|
struct stat statbuf;
|
|
int status;
|
|
|
|
if ((dirp = opendir(path)) != NULL) {
|
|
struct dirent *de;
|
|
|
|
for (de = readdir(dirp); de; de = readdir(dirp)) {
|
|
/* Don't remove '.' or '..' In theory we should do a better
|
|
* check for .., but the directories we are removing are fairly
|
|
* limited and should not have dot files in them.
|
|
*/
|
|
if (de->d_name[0] == '.')
|
|
continue;
|
|
|
|
/* Linux actually has a type field in the dirent structure,
|
|
* but that is not portable - stat should be portable
|
|
*/
|
|
status = stat(de->d_name, &statbuf);
|
|
if ((status != -1) && (S_ISDIR(statbuf.st_mode))) {
|
|
snprintf(buf, sizeof(buf), "%s/%s", path, de->d_name);
|
|
remove_directory(buf);
|
|
continue;
|
|
}
|
|
snprintf(buf, sizeof(buf), "%s/%s", path, de->d_name);
|
|
if (unlink(buf)) {
|
|
LOG(llevError, "Unable to remove %s\n", path);
|
|
}
|
|
}
|
|
closedir(dirp);
|
|
}
|
|
if (rmdir(path)) {
|
|
LOG(llevError, "Unable to remove directory %s\n", path);
|
|
}
|
|
}
|
|
|
|
#if defined(sgi)
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#define popen fixed_popen
|
|
|
|
/**
|
|
* Executes a command in the background through a call to /bin/sh.
|
|
*
|
|
* @param command
|
|
* command which will be launched.
|
|
* @param type
|
|
* whether we want to read or write to that command. Must be "r" or "w".
|
|
* @return
|
|
* pointer to stream to command, NULL on failure.
|
|
* @note
|
|
* for SGI only.
|
|
*
|
|
* @todo
|
|
* is this actually used?
|
|
*/
|
|
FILE *popen_local(const char *command, const char *type) {
|
|
int fd[2];
|
|
int pd;
|
|
FILE *ret;
|
|
if (!strcmp(type, "r")) {
|
|
pd = STDOUT_FILENO;
|
|
} else if (!strcmp(type, "w")) {
|
|
pd = STDIN_FILENO;
|
|
} else {
|
|
return NULL;
|
|
}
|
|
if (pipe(fd) != -1) {
|
|
switch (fork()) {
|
|
case -1:
|
|
close(fd[0]);
|
|
close(fd[1]);
|
|
break;
|
|
|
|
case 0:
|
|
close(fd[0]);
|
|
if ((fd[1] == pd) || (dup2(fd[1], pd) == pd)) {
|
|
if (fd[1] != pd) {
|
|
close(fd[1]);
|
|
}
|
|
execl("/bin/sh", "sh", "-c", command, NULL);
|
|
close(pd);
|
|
}
|
|
exit(1);
|
|
break;
|
|
|
|
default:
|
|
close(fd[1]);
|
|
if (ret = fdopen(fd[0], type)) {
|
|
return ret;
|
|
}
|
|
close(fd[0]);
|
|
break;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
#endif /* defined(sgi) */
|
|
|
|
/*****************************************************************************
|
|
* String related function
|
|
****************************************************************************/
|
|
|
|
/**
|
|
* A replacement of strdup(), since it's not defined at some
|
|
* unix variants.
|
|
*
|
|
* @param str
|
|
* string to duplicate.
|
|
* @return
|
|
* copy, needs to be freed by caller. NULL on memory allocation error.
|
|
*/
|
|
char *strdup_local(const char *str) {
|
|
char *c = (char *)malloc(strlen(str)+1);
|
|
if (c != NULL)
|
|
strcpy(c, str);
|
|
return c;
|
|
}
|
|
|
|
/** Converts x to number */
|
|
#define DIGIT(x) (isdigit(x) ? (x)-'0' : \
|
|
islower(x) ? (x)+10-'a' : (x)+10-'A')
|
|
#define MBASE ('z'-'a'+1+10)
|
|
|
|
#if !defined(HAVE_STRTOL)
|
|
/**
|
|
* Converts a string to long.
|
|
*
|
|
* A replacement of strtol() since it's not defined at
|
|
* many unix systems.
|
|
*
|
|
* @param str
|
|
* string to convert.
|
|
* @param ptr
|
|
* will point to first invalid character in str.
|
|
* @param base
|
|
* base to consider to convert to long.
|
|
*
|
|
* @todo
|
|
* check weird -+ handling (missing break?)
|
|
*/
|
|
long strtol(register char *str, char **ptr, register int base) {
|
|
register long val;
|
|
register int c;
|
|
int xx, neg = 0;
|
|
|
|
if (ptr != (char **)0)
|
|
*ptr = str; /* in case no number is formed */
|
|
if (base < 0 || base > MBASE)
|
|
return (0); /* base is invalid */
|
|
if (!isalnum(c = *str)) {
|
|
while (isspace(c))
|
|
c = *++str;
|
|
switch (c) {
|
|
case '-':
|
|
neg++;
|
|
case '+':
|
|
c = *++str;
|
|
}
|
|
}
|
|
if (base == 0) {
|
|
if (c != '0')
|
|
base = 10;
|
|
else {
|
|
if (str[1] == 'x' || str[1] == 'X')
|
|
base = 16;
|
|
else
|
|
base = 8;
|
|
}
|
|
}
|
|
/*
|
|
* For any base > 10, the digits incrementally following
|
|
* 9 are assumed to be "abc...z" or "ABC...Z"
|
|
*/
|
|
if (!isalnum(c) || (xx = DIGIT(c)) >= base)
|
|
return 0; /* no number formed */
|
|
if (base == 16 && c == '0' && isxdigit(str[2]) && (str[1] == 'x' || str[1] == 'X'))
|
|
c = *(str += 2); /* skip over leading "0x" or "0X" */
|
|
for (val = -DIGIT(c); isalnum(c = *++str) && (xx = DIGIT(c)) < base; )
|
|
/* accumulate neg avoids surprises near
|
|
MAXLONG */
|
|
val = base*val-xx;
|
|
if (ptr != (char **)0)
|
|
*ptr = str;
|
|
return (neg ? val : -val);
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* Case-insensitive comparaison of strings.
|
|
*
|
|
* This seems to be lacking on some system.
|
|
*
|
|
* @param s1
|
|
* @param s2
|
|
* strings to compare.
|
|
* @param n
|
|
* maximum number of chars to compare.
|
|
* @return
|
|
* @li -1 if s1 is less than s2
|
|
* @li 0 if s1 equals s2
|
|
* @li 1 if s1 is greater than s2
|
|
*/
|
|
#if !defined(HAVE_STRNCASECMP)
|
|
int strncasecmp(const char *s1, const char *s2, int n) {
|
|
register int c1, c2;
|
|
|
|
while (*s1 && *s2 && n) {
|
|
c1 = tolower(*s1);
|
|
c2 = tolower(*s2);
|
|
if (c1 != c2)
|
|
return (c1-c2);
|
|
s1++;
|
|
s2++;
|
|
n--;
|
|
}
|
|
if (!n)
|
|
return (0);
|
|
return (int)(*s1-*s2);
|
|
}
|
|
#endif
|
|
|
|
#if !defined(HAVE_STRCASECMP)
|
|
/**
|
|
* Case-insensitive comparaison of strings.
|
|
*
|
|
* This seems to be lacking on some system.
|
|
*
|
|
* @param s1
|
|
* @param s2
|
|
* strings to compare.
|
|
* @return
|
|
* @li -1 if s1 is less than s2
|
|
* @li 0 if s1 equals s2
|
|
* @li 1 if s1 is greater than s2
|
|
*/
|
|
int strcasecmp(const char *s1, const char *s2) {
|
|
register int c1, c2;
|
|
|
|
while (*s1 && *s2) {
|
|
c1 = tolower(*s1);
|
|
c2 = tolower(*s2);
|
|
if (c1 != c2)
|
|
return (c1-c2);
|
|
s1++;
|
|
s2++;
|
|
}
|
|
if (*s1 == '\0' && *s2 == '\0')
|
|
return 0;
|
|
return (int)(*s1-*s2);
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* Finds a substring in a string, in a case-insensitive manner.
|
|
*
|
|
* @param s
|
|
* string we're searching into.
|
|
* @param find
|
|
* string we're searching for.
|
|
* @return
|
|
* pointer to first occurrence of find in s, NULL if not found.
|
|
*/
|
|
const char *strcasestr_local(const char *s, const char *find) {
|
|
char c, sc;
|
|
size_t len;
|
|
|
|
if ((c = *find++) != 0) {
|
|
c = tolower(c);
|
|
len = strlen(find);
|
|
do {
|
|
do {
|
|
if ((sc = *s++) == 0)
|
|
return NULL;
|
|
} while (tolower(sc) != c);
|
|
} while (strncasecmp(s, find, len) != 0);
|
|
s--;
|
|
}
|
|
return s;
|
|
}
|
|
|
|
#if !defined(HAVE_SNPRINTF)
|
|
/**
|
|
* Formats to a string, in a size-safe way.
|
|
*
|
|
* @param dest
|
|
* where to write.
|
|
* @param max
|
|
* max length of dest.
|
|
* @param format
|
|
* format specifier, and arguments.
|
|
* @return
|
|
* number of chars written to dest.
|
|
*
|
|
* @warning
|
|
* this function will abort() if there is an overflow.
|
|
*
|
|
* @todo
|
|
* try to do something better than abort()?
|
|
*/
|
|
int snprintf(char *dest, int max, const char *format, ...) {
|
|
va_list var;
|
|
int ret;
|
|
|
|
va_start(var, format);
|
|
ret = vsprintf(dest, format, var);
|
|
va_end(var);
|
|
if (ret > max)
|
|
abort();
|
|
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
/**
|
|
* This takes an err number and returns a string with a description of
|
|
* the error.
|
|
*
|
|
* @param errnum
|
|
* error we want the description of.
|
|
* @param buf
|
|
* buffer to contain the description.
|
|
* @param size
|
|
* buf's length.
|
|
* @return
|
|
* buf.
|
|
*/
|
|
char *strerror_local(int errnum, char *buf, size_t size) {
|
|
#if defined(HAVE_STRERROR_R)
|
|
/* Then what flavour of strerror_r... */
|
|
# if defined(STRERROR_R_CHAR_P)
|
|
char *bbuf;
|
|
|
|
buf[0] = 0;
|
|
bbuf = (char *)strerror_r(errnum, buf, size);
|
|
if ((buf[0] == 0) && (bbuf != NULL))
|
|
strncpy(buf, bbuf, size);
|
|
# else
|
|
if (strerror_r(errnum, buf, size) != 0) {
|
|
/* EINVAL and ERANGE are possible errors from this strerror_r */
|
|
if (errno == ERANGE) {
|
|
strncat(buf, "Too small buffer.", size);
|
|
} else if (errno == EINVAL) {
|
|
strncat(buf, "Error number invalid.", size);
|
|
}
|
|
}
|
|
# endif /* STRERROR_R_CHAR_P */
|
|
|
|
#else /* HAVE_STRERROR_R */
|
|
|
|
# if defined(HAVE_STRERROR)
|
|
snprintf(buf, size, "%s", strerror(errnum));
|
|
# else
|
|
# error If this is C89 the compiler should have strerror!;
|
|
# endif
|
|
#endif /* HAVE_STRERROR_R */
|
|
return buf;
|
|
}
|
|
|
|
/**
|
|
* Computes the square root.
|
|
* Based on (n+1)^2 = n^2 + 2n + 1
|
|
* given that 1^2 = 1, then
|
|
* 2^2 = 1 + (2 + 1) = 1 + 3 = 4
|
|
* 3^2 = 4 + (4 + 1) = 4 + 5 = 1 + 3 + 5 = 9
|
|
* 4^2 = 9 + (6 + 1) = 9 + 7 = 1 + 3 + 5 + 7 = 16
|
|
* ...
|
|
* In other words, a square number can be express as the sum of the
|
|
* series n^2 = 1 + 3 + ... + (2n-1)
|
|
*
|
|
* @param n
|
|
* number of which to compute the root.
|
|
* @return
|
|
* square root.
|
|
*/
|
|
int isqrt(int n) {
|
|
int result, sum, prev;
|
|
|
|
result = 0;
|
|
prev = sum = 1;
|
|
while (sum <= n) {
|
|
prev += 2;
|
|
sum += prev;
|
|
++result;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* This is a list of the suffix, uncompress and compress functions. Thus,
|
|
* if you have some other compress program you want to use, the only thing
|
|
* that needs to be done is to extended this.
|
|
* The first entry must be NULL - this is what is used for non
|
|
* compressed files.
|
|
*/
|
|
const char *uncomp[NROF_COMPRESS_METHODS][3] = {
|
|
{ NULL, NULL, NULL },
|
|
{ ".Z", UNCOMPRESS, COMPRESS },
|
|
{ ".gz", GUNZIP, GZIP },
|
|
{ ".bz2", BUNZIP, BZIP }
|
|
};
|
|
|
|
/**
|
|
* Open and possibly uncompress a file.
|
|
*
|
|
* @param ext
|
|
* the extension if the file is compressed.
|
|
* @param uncompressor
|
|
* the command to uncompress the file if the file is compressed.
|
|
* @param name
|
|
* the base file name without compression extension
|
|
* @param flag
|
|
* only used for compressed files:
|
|
* @li if set, uncompress and open the file
|
|
* @li if unset, uncompress the file via pipe
|
|
* @param[out] compressed
|
|
* set to zero if the file was uncompressed
|
|
* @return
|
|
* pointer to opened file, NULL on failure.
|
|
*
|
|
* @note
|
|
* will set ::errno if an error occurs.
|
|
*/
|
|
static FILE *open_and_uncompress_file(const char *ext, const char *uncompressor, const char *name, int flag, int *compressed) {
|
|
struct stat st;
|
|
char buf[MAX_BUF];
|
|
char buf2[MAX_BUF];
|
|
int ret;
|
|
|
|
if (ext == NULL) {
|
|
ext = "";
|
|
}
|
|
|
|
if (strlen(name)+strlen(ext) >= sizeof(buf)) {
|
|
errno = ENAMETOOLONG; /* File name too long */
|
|
return NULL;
|
|
}
|
|
snprintf(buf, sizeof(buf), "%s%s", name, ext);
|
|
|
|
if (stat(buf, &st) != 0) {
|
|
return NULL;
|
|
}
|
|
|
|
if (!S_ISREG(st.st_mode)) {
|
|
errno = EISDIR; /* Not a regular file */
|
|
return NULL;
|
|
}
|
|
|
|
if (uncompressor == NULL) {
|
|
/* open without uncompression */
|
|
return fopen(buf, "rb");
|
|
}
|
|
|
|
/* The file name buf (and its substring name) is passed as an argument to a
|
|
* shell command, therefore check for characters that could confuse the
|
|
* shell.
|
|
*/
|
|
if (strpbrk(buf, "'\\\r\n") != NULL) {
|
|
errno = ENOENT; /* Pretend the file does not exist */
|
|
return NULL;
|
|
}
|
|
|
|
if (!flag) {
|
|
/* uncompress via pipe */
|
|
|
|
if (strlen(uncompressor)+4+strlen(buf)+1 >= sizeof(buf2)) {
|
|
errno = ENAMETOOLONG; /* File name too long */
|
|
return NULL;
|
|
}
|
|
snprintf(buf2, sizeof(buf2), "%s < '%s'", uncompressor, buf);
|
|
return popen(buf2, "r");
|
|
}
|
|
|
|
/* remove compression from file, then open file */
|
|
|
|
if (stat(name, &st) == 0 && !S_ISREG(st.st_mode)) {
|
|
errno = EISDIR;
|
|
return NULL;
|
|
}
|
|
|
|
if (strlen(uncompressor)+4+strlen(buf)+5+strlen(name)+1 >= sizeof(buf2)) {
|
|
errno = ENAMETOOLONG; /* File name too long */
|
|
return NULL;
|
|
}
|
|
snprintf(buf2, sizeof(buf2), "%s < '%s' > '%s'", uncompressor, buf, name);
|
|
|
|
ret = system(buf2);
|
|
if (!WIFEXITED(ret) || WEXITSTATUS(ret) != 0) {
|
|
LOG(llevError, "system(%s) returned %d\n", buf2, ret);
|
|
errno = ENOENT;
|
|
return NULL;
|
|
}
|
|
|
|
unlink(buf); /* Delete the original */
|
|
*compressed = 0; /* Change to "uncompressed file" */
|
|
chmod(name, st.st_mode); /* Copy access mode from compressed file */
|
|
|
|
return fopen(name, "rb");
|
|
}
|
|
|
|
/**
|
|
* open_and_uncompress() first searches for the original filename. If it exist,
|
|
* then it opens it and returns the file-pointer.
|
|
*
|
|
* If not, it does two things depending on the flag. If the flag is set, it
|
|
* tries to create the original file by appending a compression suffix to name
|
|
* and uncompressing it. If the flag is not set, it creates a pipe that is used
|
|
* for reading the file (NOTE - you can not use fseek on pipes).
|
|
*
|
|
* The compressed pointer is set to nonzero if the file is compressed (and
|
|
* thus, fp is actually a pipe.) It returns 0 if it is a normal file.
|
|
*
|
|
* @param name
|
|
* the base file name without compression extension
|
|
* @param flag
|
|
* only used for compressed files:
|
|
* @li if set, uncompress and open the file
|
|
* @li if unset, uncompress the file via pipe
|
|
* @param[out] compressed
|
|
* set to zero if the file was uncompressed
|
|
* @return
|
|
* pointer to opened file, NULL on failure.
|
|
*
|
|
* @note
|
|
* will set ::errno if an error occurs.
|
|
*/
|
|
FILE *open_and_uncompress(const char *name, int flag, int *compressed) {
|
|
size_t i;
|
|
FILE *fp;
|
|
|
|
for (i = 0; i < NROF_COMPRESS_METHODS; i++) {
|
|
*compressed = i;
|
|
fp = open_and_uncompress_file(uncomp[i][0], uncomp[i][1], name, flag, compressed);
|
|
if (fp != NULL) {
|
|
return fp;
|
|
}
|
|
}
|
|
|
|
errno = ENOENT;
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* Closes specified file.
|
|
*
|
|
* @param fp
|
|
* file to close.
|
|
* @param compressed
|
|
* whether the file was compressed or not. Set by open_and_uncompress().
|
|
*/
|
|
void close_and_delete(FILE *fp, int compressed) {
|
|
if (compressed)
|
|
pclose(fp);
|
|
else
|
|
fclose(fp);
|
|
}
|
|
|
|
/**
|
|
* Checks if any directories in the given path doesn't exist, and creates if necessary.
|
|
*
|
|
* @param filename
|
|
* file path we'll want to access. Can be NULL.
|
|
*
|
|
* @note
|
|
* will LOG() to debug and error.
|
|
*/
|
|
void make_path_to_file(const char *filename) {
|
|
char buf[MAX_BUF], *cp = buf;
|
|
struct stat statbuf;
|
|
|
|
if (!filename || !*filename)
|
|
return;
|
|
|
|
strcpy(buf, filename);
|
|
LOG(llevDebug, "make_path_tofile %s...\n", filename);
|
|
while ((cp = strchr(cp+1, (int)'/'))) {
|
|
*cp = '\0';
|
|
if (stat(buf, &statbuf) || !S_ISDIR(statbuf.st_mode)) {
|
|
LOG(llevDebug, "Was not dir: %s\n", buf);
|
|
if (mkdir(buf, SAVE_DIR_MODE)) {
|
|
char err[MAX_BUF];
|
|
|
|
LOG(llevError, "Cannot mkdir %s: %s\n", buf, strerror_local(errno, err, sizeof(err)));
|
|
return;
|
|
}
|
|
}
|
|
*cp = '/';
|
|
}
|
|
}
|