summaryrefslogtreecommitdiff
path: root/src/libs/file
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/file')
-rw-r--r--src/libs/file/Makeinfo2
-rw-r--r--src/libs/file/dirs.c830
-rw-r--r--src/libs/file/files.c165
-rw-r--r--src/libs/file/filintrn.h24
-rw-r--r--src/libs/file/temp.c199
5 files changed, 1220 insertions, 0 deletions
diff --git a/src/libs/file/Makeinfo b/src/libs/file/Makeinfo
new file mode 100644
index 0000000..b4ea11d
--- /dev/null
+++ b/src/libs/file/Makeinfo
@@ -0,0 +1,2 @@
+uqm_CFILES="dirs.c files.c"
+uqm_HFILES="filintrn.h"
diff --git a/src/libs/file/dirs.c b/src/libs/file/dirs.c
new file mode 100644
index 0000000..39a44f5
--- /dev/null
+++ b/src/libs/file/dirs.c
@@ -0,0 +1,830 @@
+/*
+ * 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.
+ */
+
+// Contains code handling directories
+
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include "port.h"
+#include "config.h"
+#include "filintrn.h"
+#include "libs/compiler.h"
+#include "libs/memlib.h"
+#include "libs/misc.h"
+#include "libs/log.h"
+
+#ifdef HAVE_DRIVE_LETTERS
+# include <ctype.h>
+ // For tolower()
+#endif /* HAVE_DRIVE_LETTERS */
+#ifdef WIN32
+# include <direct.h>
+ // For _getdcwd()
+#else
+# include <pwd.h>
+ // For getpwuid()
+#endif
+
+/* Try to find a suitable value for %APPDATA% if it isn't defined on
+ * Windows.
+ */
+#define APPDATA_FALLBACK
+
+
+static char *expandPathAbsolute (char *dest, size_t destLen, const char *src,
+ size_t *skipSrc, int what);
+static char *strrchr2(const char *start, int c, const char *end);
+
+
+int
+createDirectory(const char *dir, int mode)
+{
+ return MKDIR(dir, mode);
+}
+
+// make all components of the path if they don't exist already
+// returns 0 on success, -1 on failure.
+// on failure, some parts may still have been created.
+int
+mkdirhier (const char *path)
+{
+ char *buf; // buffer
+ char *ptr; // end of the string in buf
+ const char *pathstart; // start of a component of path
+ const char *pathend; // first char past the end of a component of path
+ size_t len;
+ struct stat statbuf;
+
+ len = strlen (path);
+ buf = HMalloc (len + 2); // one extra for possibly added '/'
+
+ ptr = buf;
+ pathstart = path;
+
+#ifdef HAVE_DRIVE_LETTERS
+ if (isDriveLetter(pathstart[0]) && pathstart[1] == ':')
+ {
+ // Driveletter + semicolon on Windows.
+ // Copy as is; don't try to create directories for it.
+ *(ptr++) = *(pathstart++);
+ *(ptr++) = *(pathstart++);
+
+ ptr[0] = '/';
+ ptr[1] = '\0';
+ if (stat (buf, &statbuf) == -1)
+ {
+ log_add (log_Error, "Can't stat \"%s\": %s", buf, strerror (errno));
+ goto err;
+ }
+ }
+ else
+#endif /* HAVE_DRIVE_LETTERS */
+#ifdef HAVE_UNC_PATHS
+ if (pathstart[0] == '\\' && pathstart[1] == '\\')
+ {
+ // Universal Naming Convention path. (\\server\share\...)
+ // Copy the server part as is; don't try to create directories for
+ // it, or stat it. Don't create a dir for the share either.
+ *(ptr++) = *(pathstart++);
+ *(ptr++) = *(pathstart++);
+
+ // Copy the server part
+ while (*pathstart != '\0' && *pathstart != '\\' && *pathstart != '/')
+ *(ptr++) = *(pathstart++);
+
+ if (*pathstart == '\0')
+ {
+ log_add (log_Error, "Incomplete UNC path \"%s\"", pathstart);
+ goto err;
+ }
+
+ // Copy the path seperator.
+ *(ptr++) = *(pathstart++);
+
+ // Copy the share part
+ while (*pathstart != '\0' && *pathstart != '\\' && *pathstart != '/')
+ *(ptr++) = *(pathstart++);
+
+ ptr[0] = '/';
+ ptr[1] = '\0';
+ if (stat (buf, &statbuf) == -1)
+ {
+ log_add (log_Error, "Can't stat \"%s\": %s", buf, strerror (errno));
+ goto err;
+ }
+ }
+#else
+ {
+ // Making sure that there is an 'else' case if HAVE_DRIVE_LETTERS is
+ // defined.
+ }
+#endif /* HAVE_UNC_PATHS */
+
+ if (*pathstart == '/')
+ *(ptr++) = *(pathstart++);
+
+ if (*pathstart == '\0') {
+ // path exists completely, nothing more to do
+ goto success;
+ }
+
+ // walk through the path as long as the components exist
+ while (1)
+ {
+ pathend = strchr (pathstart, '/');
+ if (pathend == NULL)
+ pathend = path + len;
+ memcpy(ptr, pathstart, pathend - pathstart);
+ ptr += pathend - pathstart;
+ *ptr = '\0';
+
+ if (stat (buf, &statbuf) == -1)
+ {
+ if (errno == ENOENT)
+ break;
+#ifdef __SYMBIAN32__
+ // XXX: HACK: If we don't have access to a directory, we can
+ // still have access to the underlying entries. We don't
+ // actually know whether the entry is a directory, but I know of
+ // no way to find out. We just pretend that it is; if we were
+ // wrong, an error will occur when we try to do something with
+ // the directory. That /should/ not be a problem, as any such
+ // action should have its own error checking.
+ if (errno != EACCES)
+#endif
+ {
+ log_add (log_Error, "Can't stat \"%s\": %s", buf,
+ strerror (errno));
+ goto err;
+ }
+ }
+
+ if (*pathend == '\0')
+ goto success;
+
+ *ptr = '/';
+ ptr++;
+ pathstart = pathend + 1;
+ while (*pathstart == '/')
+ pathstart++;
+ // pathstart is the next non-slash character
+
+ if (*pathstart == '\0')
+ goto success;
+ }
+
+ // create all components left
+ while (1)
+ {
+ if (createDirectory (buf, 0777) == -1)
+ {
+ log_add (log_Error, "Error: Can't create %s: %s", buf,
+ strerror (errno));
+ goto err;
+ }
+
+ if (*pathend == '\0')
+ break;
+
+ *ptr = '/';
+ ptr++;
+ pathstart = pathend + 1;
+ while (*pathstart == '/')
+ pathstart++;
+ // pathstart is the next non-slash character
+
+ if (*pathstart == '\0')
+ break;
+
+ pathend = strchr (pathstart, '/');
+ if (pathend == NULL)
+ pathend = path + len;
+
+ memcpy (ptr, pathstart, pathend - pathstart);
+ ptr += pathend - pathstart;
+ *ptr = '\0';
+ }
+
+success:
+ HFree (buf);
+ return 0;
+
+err:
+ {
+ int savedErrno = errno;
+ HFree (buf);
+ errno = savedErrno;
+ }
+ return -1;
+}
+
+// Get the user's home dir
+// returns a pointer to a static buffer from either getenv() or getpwuid().
+const char *
+getHomeDir (void)
+{
+#ifdef WIN32
+ return getenv ("HOME");
+#else
+ const char *home;
+ struct passwd *pw;
+
+ home = getenv ("HOME");
+ if (home != NULL)
+ return home;
+
+ pw = getpwuid (getuid ());
+ if (pw == NULL)
+ return NULL;
+ // NB: pw points to a static buffer.
+
+ return pw->pw_dir;
+#endif
+}
+
+// Performs various types of string expansions on a path.
+// 'what' is an OR'd compination of the folowing flags, which
+// specify what type of exmansions will be performed.
+// EP_HOME - Expand '~' for home dirs.
+// EP_ABSOLUTE - Make relative paths absolute
+// EP_ENVVARS - Expand environment variables
+// EP_DOTS - Process ".." and "."
+// EP_SLASHES - Consider backslashes as path component separators.
+// They will be replaced by slashes.
+// EP_SINGLESEP - Replace multiple consecutive path seperators (which POSIX
+// considers equivalent to a single one) by a single one.
+// Additionally, there's EP_ALL, which indicates all of the above,
+// and EP_ALL_SYSTEM, which does the same as EP_ALL, with the exception
+// of EP_SLASHES, which will only be included if the operating system
+// accepts backslashes as path terminators.
+// Returns 0 on success.
+// Returns -1 on failure, setting errno.
+int
+expandPath (char *dest, size_t len, const char *src, int what)
+{
+ char *destptr, *destend;
+ char *buf = NULL;
+ char *bufptr, *bufend;
+ const char *srcend;
+
+#define CHECKLEN(bufname, n) \
+ if (bufname##ptr + (n) >= bufname##end) \
+ { \
+ errno = ENAMETOOLONG; \
+ goto err; \
+ } \
+ else \
+ (void) 0
+
+ destptr = dest;
+ destend = dest + len;
+
+ if (what & EP_ENVVARS)
+ {
+ buf = HMalloc (len);
+ bufptr = buf;
+ bufend = buf + len;
+ while (*src != '\0')
+ {
+ switch (*src)
+ {
+#ifdef WIN32
+ case '%':
+ {
+ /* Environment variable substitution in Windows */
+ const char *end; // end of env var name in src
+ const char *envVar;
+ char *envName;
+ size_t envNameLen, envVarLen;
+
+ src++;
+ end = strchr (src, '%');
+ if (end == NULL)
+ {
+ errno = EINVAL;
+ goto err;
+ }
+
+ envNameLen = end - src;
+ envName = HMalloc (envNameLen + 1);
+ memcpy (envName, src, envNameLen + 1);
+ envName[envNameLen] = '\0';
+ envVar = getenv (envName);
+ HFree (envName);
+
+ if (envVar == NULL)
+ {
+#ifdef APPDATA_FALLBACK
+ if (strncmp (src, "APPDATA", envNameLen) != 0)
+ {
+ // Substitute an empty string
+ src = end + 1;
+ break;
+ }
+
+ // fallback for when the APPDATA env var is not set
+ // Using SHGetFolderPath or SHGetSpecialFolderPath
+ // is problematic (not everywhere available).
+ log_add (log_Warning, "Warning: %%APPDATA%% is not set. "
+ "Falling back to \"%%USERPROFILE%%\\Application "
+ "Data\"");
+ envVar = getenv ("USERPROFILE");
+ if (envVar != NULL)
+ {
+#define APPDATA_STRING "\\Application Data"
+ envVarLen = strlen (envVar);
+ CHECKLEN (buf,
+ envVarLen + sizeof (APPDATA_STRING) - 1);
+ strcpy (bufptr, envVar);
+ bufptr += envVarLen;
+ strcpy (bufptr, APPDATA_STRING);
+ bufptr += sizeof (APPDATA_STRING) - 1;
+ src = end + 1;
+ break;
+ }
+
+ // fallback to "./userdata"
+#define APPDATA_FALLBACK_STRING ".\\userdata"
+ log_add (log_Warning,
+ "Warning: %%USERPROFILE%% is not set. "
+ "Falling back to \"%s\" for %%APPDATA%%",
+ APPDATA_FALLBACK_STRING);
+ CHECKLEN (buf, sizeof (APPDATA_FALLBACK_STRING) - 1);
+ strcpy (bufptr, APPDATA_FALLBACK_STRING);
+ bufptr += sizeof (APPDATA_FALLBACK_STRING) - 1;
+ src = end + 1;
+ break;
+
+#else /* !defined (APPDATA_FALLBACK) */
+ // Substitute an empty string
+ src = end + 1;
+ break;
+#endif /* APPDATA_FALLBACK */
+ }
+
+ envVarLen = strlen (envVar);
+ CHECKLEN (buf, envVarLen);
+ strcpy (bufptr, envVar);
+ bufptr += envVarLen;
+ src = end + 1;
+ break;
+ }
+#endif
+#ifndef WIN32
+ case '$':
+ {
+ const char *end;
+ char *envName;
+ size_t envNameLen;
+ const char *envVar;
+ size_t envVarLen;
+
+ src++;
+ if (*src == '{')
+ {
+ src++;
+ end = strchr(src, '}');
+ if (end == NULL)
+ {
+ errno = EINVAL;
+ goto err;
+ }
+ envNameLen = end - src;
+ end++; // Skip the '}'
+ }
+ else
+ {
+ end = src;
+ while ((*end >= 'A' && *end <= 'Z') ||
+ (*end >= 'a' && *end <= 'z') ||
+ (*end >= '0' && *end <= '9') ||
+ *end == '_')
+ end++;
+ envNameLen = end - src;
+ }
+
+ envName = HMalloc (envNameLen + 1);
+ memcpy (envName, src, envNameLen + 1);
+ envName[envNameLen] = '\0';
+ envVar = getenv (envName);
+ HFree (envName);
+
+ if (envVar != NULL)
+ {
+ envVarLen = strlen (envVar);
+ CHECKLEN (buf, envVarLen);
+ memcpy (bufptr, envVar, envVarLen);
+ bufptr += envVarLen;
+ }
+
+ src = end;
+ break;
+ }
+#endif
+ default:
+ CHECKLEN(buf, 1);
+ *(bufptr++) = *(src++);
+ break;
+ } // switch
+ } // while
+ *bufptr = '\0';
+ src = buf;
+ srcend = bufptr;
+ } // if (what & EP_ENVVARS)
+ else
+ srcend = src + strlen (src);
+
+ if (what & EP_HOME)
+ {
+ if (src[0] == '~')
+ {
+ const char *home;
+ size_t homelen;
+
+ if (src[1] != '/')
+ {
+ errno = EINVAL;
+ goto err;
+ }
+
+ home = getHomeDir ();
+ if (home == NULL)
+ {
+ errno = ENOENT;
+ goto err;
+ }
+ homelen = strlen (home);
+
+ if (what & EP_ABSOLUTE) {
+ size_t skip;
+ destptr = expandPathAbsolute (dest, destend - dest,
+ home, &skip, what);
+ if (destptr == NULL)
+ {
+ // errno is set
+ goto err;
+ }
+ home += skip;
+ what &= ~EP_ABSOLUTE;
+ // The part after the '~' should not be seen
+ // as absolute.
+ }
+
+ CHECKLEN (dest, homelen);
+ memcpy (destptr, home, homelen);
+ destptr += homelen;
+ src++; /* skip the ~ */
+ }
+ }
+
+ if (what & EP_ABSOLUTE)
+ {
+ size_t skip;
+ destptr = expandPathAbsolute (destptr, destend - destptr, src,
+ &skip, what);
+ if (destptr == NULL)
+ {
+ // errno is set
+ goto err;
+ }
+ src += skip;
+ }
+
+ CHECKLEN (dest, srcend - src);
+ memcpy (destptr, src, srcend - src + 1);
+ // The +1 is for the '\0'. It is already taken into account by
+ // CHECKLEN.
+
+ if (what & EP_SLASHES)
+ {
+ /* Replacing backslashes in path by slashes. */
+ destptr = dest;
+#ifdef HAVE_UNC_PATHS
+ {
+ // A UNC path should always start with two backslashes
+ // and have a backslash in between the server and share part.
+ size_t skip = skipUNCServerShare (destptr);
+ if (skip != 0)
+ {
+ char *slash = (char *) memchr (destptr + 2, '/', skip - 2);
+ if (slash)
+ *slash = '\\';
+ destptr += skip;
+ }
+ }
+#endif /* HAVE_UNC_PATHS */
+ while (*destptr != '\0')
+ {
+ if (*destptr == '\\')
+ *destptr = '/';
+ destptr++;
+ }
+ }
+
+ if (what & EP_DOTS) {
+ // At this point backslashes are already replaced by slashes if they
+ // are specified to be path seperators.
+ // Note that the path can only get smaller, so no size checks
+ // need to be done.
+ char *pathStart;
+ // Start of the first path component, after any
+ // leading slashes or drive letters.
+ char *startPart;
+ char *endPart;
+
+ pathStart = dest;
+#ifdef HAVE_DRIVE_LETTERS
+ if (isDriveLetter(pathStart[0]) && (pathStart[1] == ':'))
+ {
+ pathStart += 2;
+ }
+ else
+#endif /* HAVE_DRIVE_LETTERS */
+#ifdef HAVE_UNC_PATHS
+ {
+ // Test for a Universal Naming Convention path.
+ pathStart += skipUNCServerShare(pathStart);
+ }
+#else
+ {
+ // Making sure that there is an 'else' case if HAVE_DRIVE_LETTERS is
+ // defined.
+ }
+#endif /* HAVE_UNC_PATHS */
+ if (pathStart[0] == '/')
+ pathStart++;
+
+ startPart = pathStart;
+ destptr = pathStart;
+ for (;;)
+ {
+ endPart = strchr(startPart, '/');
+ if (endPart == NULL)
+ endPart = startPart + strlen(startPart);
+
+ if (endPart - startPart == 1 && startPart[0] == '.')
+ {
+ // Found "." as path component. Ignore this component.
+ }
+ else if (endPart - startPart == 2 &&
+ startPart[0] == '.' && startPart[1] == '.')
+ {
+ // Found ".." as path component. Remove the previous
+ // component, and ignore this one.
+ char *lastSlash;
+ lastSlash = strrchr2(pathStart, '/', destptr - 1);
+ if (lastSlash == NULL)
+ {
+ if (destptr == pathStart)
+ {
+ // We ran out of path components to back out of.
+ errno = EINVAL;
+ goto err;
+ }
+ destptr = pathStart;
+ }
+ else
+ {
+ destptr = lastSlash;
+ if (*endPart == '/')
+ destptr++;
+ }
+ }
+ else
+ {
+ // A normal path component; copy it.
+ // Using memmove as source and destination may overlap.
+ memmove(destptr, startPart, endPart - startPart);
+ destptr += (endPart - startPart);
+ if (*endPart == '/')
+ {
+ *destptr = '/';
+ destptr++;
+ }
+ }
+ if (*endPart == '\0')
+ break;
+ startPart = endPart + 1;
+ }
+ *destptr = '\0';
+ }
+
+ if (what & EP_SINGLESEP)
+ {
+ char *srcptr;
+ srcptr = dest;
+ destptr = dest;
+ while (*srcptr != '\0')
+ {
+ char ch = *srcptr;
+ *(destptr++) = *(srcptr++);
+ if (ch == '/')
+ {
+ while (*srcptr == '/')
+ srcptr++;
+ }
+ }
+ *destptr = '\0';
+ }
+
+ HFree (buf);
+ return 0;
+
+err:
+ if (buf != NULL) {
+ int savedErrno = errno;
+ HFree (buf);
+ errno = savedErrno;
+ }
+ return -1;
+}
+
+#if defined(HAVE_DRIVE_LETTERS) && defined(HAVE_CWD_PER_DRIVE)
+ // This code is only needed if we have a current working directory
+ // per drive.
+// letter is 0 based: 0 = A, 1 = B, ...
+static bool
+driveLetterExists(int letter)
+{
+ unsigned long drives;
+
+ drives = _getdrives ();
+
+ return ((drives >> letter) & 1) != 0;
+}
+#endif /* if defined(HAVE_DRIVE_LETTERS) && defined(HAVE_CWD_PER_DRIVE) */
+
+// helper for expandPath, expanding an absolute path
+// returns a pointer to the end of the filled in part of dest.
+static char *
+expandPathAbsolute (char *dest, size_t destLen, const char *src,
+ size_t *skipSrc, int what)
+{
+ const char *orgSrc;
+
+ if (src[0] == '/' || ((what & EP_SLASHES) && src[0] == '\\'))
+ {
+ // Path is already absolute; nothing to do
+ *skipSrc = 0;
+ return dest;
+ }
+
+ orgSrc = src;
+#ifdef HAVE_DRIVE_LETTERS
+ if (isDriveLetter(src[0]) && (src[1] == ':'))
+ {
+ int letter;
+
+ if (src[2] == '/' || src[2] == '\\')
+ {
+ // Path is already absolute (of the form "d:/"); nothing to do
+ *skipSrc = 0;
+ return dest;
+ }
+
+ // Path is of the form "d:path", without a (back)slash after the
+ // semicolon.
+
+#ifdef REJECT_DRIVE_PATH_WITHOUT_SLASH
+ // We reject paths of the form "d:foo/bar".
+ errno = EINVAL;
+ return NULL;
+#elif defined(HAVE_CWD_PER_DRIVE)
+ // Paths of the form "d:foo/bar" are treated as "foo/bar" relative
+ // to the working directory of d:.
+ letter = tolower(src[0]) - 'a';
+
+ // _getdcwd() should only be called on drives that exist.
+ // This is weird though, because it means a race condition
+ // in between the existance check and the call to _getdcwd()
+ // cannot be avoided, unless a drive still exists for Windows
+ // when the physical drive is removed.
+ if (!driveLetterExists (letter))
+ {
+ errno = ENOENT;
+ return NULL;
+ }
+
+ // Get the working directory for a specific drive.
+ if (_getdcwd (letter + 1, dest, destLen) == NULL)
+ {
+ // errno is set
+ return NULL;
+ }
+
+ src += 2;
+#else /* if !defined(HAVE_CWD_PER_DRIVE) */
+ // We treat paths of the form "d:foo/bar" as "d:/foo/bar".
+ if (destLen < 3) {
+ errno = ERANGE;
+ return NULL;
+ }
+ dest[0] = src[0];
+ dest[1] = ':';
+ dest[2] = '/';
+ *skipSrc = 2;
+ dest += 3;
+ return dest;
+#endif /* HAVE_CWD_PER_DRIVE */
+ }
+ else
+#endif /* HAVE_DRIVE_LETTERS */
+ {
+ // Relative dir
+ if (getcwd (dest, destLen) == NULL)
+ {
+ // errno is set
+ return NULL;
+ }
+ }
+
+ {
+ size_t tempLen;
+ tempLen = strlen (dest);
+ if (tempLen == 0)
+ {
+ // getcwd() or _getdcwd() returned a 0-length string.
+ errno = ENOENT;
+ return NULL;
+ }
+ dest += tempLen;
+ destLen -= tempLen;
+ }
+ if (dest[-1] != '/'
+#ifdef BACKSLASH_IS_PATH_SEPARATOR
+ && dest[-1] != '\\'
+#endif /* BACKSLASH_IS_PATH_SEPARATOR */
+ )
+ {
+ // Need to add a slash.
+ // There's always space, as we overwrite the '\0' that getcwd()
+ // always returns.
+ dest[0] = '/';
+ dest++;
+ destLen--;
+ }
+
+ *skipSrc = (size_t) (src - orgSrc);
+ return dest;
+}
+
+// As strrchr, but starts searching from the indicated end of the string.
+static char *
+strrchr2(const char *start, int c, const char *end) {
+ for (;;) {
+ end--;
+ if (end < start)
+ return (char *) NULL;
+ if (*end == c)
+ return (char *) unconst(end);
+ }
+}
+
+#ifdef HAVE_UNC_PATHS
+// returns 0 if the path is not a valid UNC path.
+// Does not skip trailing slashes.
+size_t
+skipUNCServerShare(const char *inPath) {
+ const char *path = inPath;
+
+ // Skip the initial two backslashes.
+ if (path[0] != '\\' || path[1] != '\\')
+ return (size_t) 0;
+ path += 2;
+
+ // Skip the server part.
+ while (*path != '\\' && *path != '/') {
+ if (*path == '\0')
+ return (size_t) 0;
+ path++;
+ }
+
+ // Skip the seperator.
+ path++;
+
+ // Skip the share part.
+ while (*path != '\0' && *path != '\\' && *path != '/')
+ path++;
+
+ return (size_t) (path - inPath);
+}
+#endif /* HAVE_UNC_PATHS */
+
+
diff --git a/src/libs/file/files.c b/src/libs/file/files.c
new file mode 100644
index 0000000..5ce7dac
--- /dev/null
+++ b/src/libs/file/files.c
@@ -0,0 +1,165 @@
+/*
+ * 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.
+ */
+
+// Contains code handling files
+
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "port.h"
+#include "libs/uio.h"
+#include "config.h"
+#include "types.h"
+#include "filintrn.h"
+#include "libs/memlib.h"
+#include "libs/log.h"
+
+static int copyError(uio_Handle *srcHandle, uio_Handle *dstHandle,
+ uio_DirHandle *unlinkHandle, const char *unlinkPath, uint8 *buf);
+
+bool
+fileExists (const char *name)
+{
+ return access (name, F_OK) == 0;
+}
+
+bool
+fileExists2(uio_DirHandle *dir, const char *fileName)
+{
+ uio_Stream *stream;
+
+ stream = uio_fopen (dir, fileName, "rb");
+ if (stream == NULL)
+ return 0;
+
+ uio_fclose (stream);
+ return 1;
+}
+
+/*
+ * Copy a file with path srcName to a file with name newName.
+ * If the destination already exists, the operation fails.
+ * Links are followed.
+ * Special files (fifos, char devices, block devices, etc) will be
+ * read as long as there is data available and the destination will be
+ * a regular file with that data.
+ * The new file will have the same permissions as the old.
+ * If an error occurs during copying, an attempt will be made to
+ * remove the copy.
+ */
+int
+copyFile (uio_DirHandle *srcDir, const char *srcName,
+ uio_DirHandle *dstDir, const char *newName)
+{
+ uio_Handle *src, *dst;
+ struct stat sb;
+#define BUFSIZE 65536
+ uint8 *buf, *bufPtr;
+ ssize_t numInBuf, numWritten;
+
+ src = uio_open (srcDir, srcName, O_RDONLY
+#ifdef WIN32
+ | O_BINARY
+#endif
+ , 0);
+ if (src == NULL)
+ return -1;
+
+ if (uio_fstat (src, &sb) == -1)
+ return copyError (src, NULL, NULL, NULL, NULL);
+
+ dst = uio_open (dstDir, newName, O_WRONLY | O_CREAT | O_EXCL
+#ifdef WIN32
+ | O_BINARY
+#endif
+ , sb.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO));
+ if (dst == NULL)
+ return copyError (src, NULL, NULL, NULL, NULL);
+
+ buf = HMalloc(BUFSIZE);
+ // This was originally a statically allocated buffer,
+ // but as this function might be run from a thread with
+ // a small stack, this is better.
+ while (1)
+ {
+ numInBuf = uio_read (src, buf, BUFSIZE);
+ if (numInBuf == -1)
+ {
+ if (errno == EINTR)
+ continue;
+ return copyError (src, dst, dstDir, newName, buf);
+ }
+ if (numInBuf == 0)
+ break;
+
+ bufPtr = buf;
+ do
+ {
+ numWritten = uio_write (dst, bufPtr, numInBuf);
+ if (numWritten == -1)
+ {
+ if (errno == EINTR)
+ continue;
+ return copyError (src, dst, dstDir, newName, buf);
+ }
+ numInBuf -= numWritten;
+ bufPtr += numWritten;
+ } while (numInBuf > 0);
+ }
+
+ HFree (buf);
+ uio_close (src);
+ uio_close (dst);
+ errno = 0;
+ return 0;
+}
+
+/*
+ * Closes srcHandle if it's not -1.
+ * Closes dstHandle if it's not -1.
+ * Removes unlinkpath from the unlinkHandle dir if it's not NULL.
+ * Frees 'buf' if not NULL.
+ * Always returns -1.
+ * errno is what was before the call.
+ */
+static int
+copyError(uio_Handle *srcHandle, uio_Handle *dstHandle,
+ uio_DirHandle *unlinkHandle, const char *unlinkPath, uint8 *buf)
+{
+ int savedErrno;
+
+ savedErrno = errno;
+
+ log_add (log_Debug, "Error while copying: %s", strerror (errno));
+
+ if (srcHandle != NULL)
+ uio_close (srcHandle);
+
+ if (dstHandle != NULL)
+ uio_close (dstHandle);
+
+ if (unlinkPath != NULL)
+ uio_unlink (unlinkHandle, unlinkPath);
+
+ if (buf != NULL)
+ HFree(buf);
+
+ errno = savedErrno;
+ return -1;
+}
+
diff --git a/src/libs/file/filintrn.h b/src/libs/file/filintrn.h
new file mode 100644
index 0000000..2c6512a
--- /dev/null
+++ b/src/libs/file/filintrn.h
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+
+// Contains code handling temporary files and dirs
+
+#ifndef _FILEINTRN_H
+
+#include "../file.h"
+
+#endif /* _FILEINTRN_H */
+
diff --git a/src/libs/file/temp.c b/src/libs/file/temp.c
new file mode 100644
index 0000000..3253e0d
--- /dev/null
+++ b/src/libs/file/temp.c
@@ -0,0 +1,199 @@
+/*
+ * 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.
+ */
+
+// Contains code handling temporary files and dirs
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#ifdef WIN32
+# include <io.h>
+#endif
+#include <string.h>
+#include <time.h>
+#include "filintrn.h"
+#include "libs/timelib.h"
+#include "port.h"
+#include "libs/compiler.h"
+#include "libs/log.h"
+#include "libs/memlib.h"
+
+static char *tempDirName;
+uio_DirHandle *tempDir;
+
+static void
+removeTempDir (void)
+{
+ rmdir (tempDirName);
+}
+
+// Try if the null-terminated path 'dir' to a directory is valid
+// as temp path.
+// On success, 'buf' will be filled with the path, with a trailing /,
+// null-terminated, and 0 is returned.
+// On failure, EINVAL, ENAMETOOLONG, or one of the errors access() can return
+// is returned, and the contents of buf is unspecified.
+static int
+tryTempDir (char *buf, size_t buflen, const char *dir)
+{
+ size_t len;
+ int haveSlash;
+
+ if (dir == NULL)
+ return EINVAL;
+
+ if (dir[0] == '\0')
+ return EINVAL;
+
+ len = strlen (dir);
+ haveSlash = (dir[len - 1] == '/'
+#ifdef WIN32
+ || dir[len - 1] == '\\'
+#endif
+ );
+ if ((haveSlash ? len : len + 1) >= buflen)
+ return ENAMETOOLONG;
+
+ strcpy (buf, dir);
+#if 0
+ //def WIN32
+ {
+ char *bufPtr;
+ for (bufPtr = buf; *bufPtr != '\0'; bufPtr++)
+ {
+ if (*bufPtr == '\\')
+ *bufPtr = '/';
+ }
+ }
+#endif
+ if (!haveSlash)
+ {
+ buf[len] = '/';
+ len++;
+ buf[len] = '\0';
+ }
+ if (access (buf, R_OK | W_OK) == -1)
+ return errno;
+
+ return 0;
+}
+
+static void
+getTempDir (char *buf, size_t buflen) {
+ char cwd[PATH_MAX];
+
+ if (tryTempDir (buf, buflen, getenv("TMP")) &&
+ tryTempDir (buf, buflen, getenv("TEMP")) &&
+#if !defined(WIN32) || defined (__CYGWIN__)
+ tryTempDir (buf, buflen, "/tmp/") &&
+ tryTempDir (buf, buflen, "/var/tmp/") &&
+#endif
+ tryTempDir (buf, buflen, getcwd (cwd, sizeof cwd)))
+ {
+ log_add (log_Fatal, "Fatal Error: Cannot find a suitable location "
+ "to store temporary files.");
+ exit (EXIT_FAILURE);
+ }
+}
+
+// Sets the global var 'tempDir'
+static int
+mountTempDir(const char *name) {
+ static uio_AutoMount *autoMount[] = { NULL };
+ uio_MountHandle *tempHandle;
+ extern uio_Repository *repository;
+
+ tempHandle = uio_mountDir (repository, "/tmp/",
+ uio_FSTYPE_STDIO, NULL, NULL, name, autoMount,
+ uio_MOUNT_TOP, NULL);
+ if (tempHandle == NULL) {
+ int saveErrno = errno;
+ log_add (log_Fatal, "Fatal error: Couldn't mount temp dir '%s': "
+ "%s", name, strerror (errno));
+ errno = saveErrno;
+ return -1;
+ }
+
+ tempDir = uio_openDir (repository, "/tmp", 0);
+ if (tempDir == NULL) {
+ int saveErrno = errno;
+ log_add (log_Fatal, "Fatal error: Could not open temp dir: %s",
+ strerror (errno));
+ errno = saveErrno;
+ return -1;
+ }
+ return 0;
+}
+
+#define NUM_TEMP_RETRIES 16
+ // Number of files to try to open before giving up.
+void
+initTempDir (void) {
+ size_t len;
+ DWORD num;
+ int i;
+ char *tempPtr;
+ // Pointer to the location in the tempDirName string where the
+ // path to the temp dir ends and the dir starts.
+
+ tempDirName = HMalloc (PATH_MAX);
+ getTempDir (tempDirName, PATH_MAX - 21);
+ // reserve 8 chars for dirname, 1 for slash, and 12 for filename
+ len = strlen(tempDirName);
+
+ num = ((DWORD) time (NULL));
+// num = GetTimeCounter () % 0xffffffff;
+ tempPtr = tempDirName + len;
+ for (i = 0; i < NUM_TEMP_RETRIES; i++)
+ {
+ sprintf (tempPtr, "%08x", num + i);
+ if (createDirectory (tempDirName, 0700) == -1)
+ continue;
+
+ // Success, we've got a temp dir.
+ tempDirName = HRealloc (tempDirName, len + 9);
+ atexit (removeTempDir);
+ if (mountTempDir (tempDirName) == -1)
+ exit (EXIT_FAILURE);
+ return;
+ }
+
+ // Failure, could not make a temporary directory.
+ log_add (log_Fatal, "Fatal error: Cannot get a name for a temporary "
+ "directory.");
+ exit (EXIT_FAILURE);
+}
+
+void
+unInitTempDir (void) {
+ uio_closeDir(tempDir);
+ // the removing of the dir is handled via atexit
+}
+
+// return the path to a file in the temp dir with the specified filename.
+// returns a pointer to a static buffer.
+char *
+tempFilePath (const char *filename) {
+ static char file[PATH_MAX];
+
+ if (snprintf (file, PATH_MAX, "%s/%s", tempDirName, filename) == -1) {
+ log_add (log_Fatal, "Path to temp file too long.");
+ exit (EXIT_FAILURE);
+ }
+ return file;
+}
+
+