aboutsummaryrefslogtreecommitdiffstats
path: root/android/utils/path.c
diff options
context:
space:
mode:
Diffstat (limited to 'android/utils/path.c')
-rw-r--r--android/utils/path.c559
1 files changed, 559 insertions, 0 deletions
diff --git a/android/utils/path.c b/android/utils/path.c
new file mode 100644
index 0000000..b15b6de
--- /dev/null
+++ b/android/utils/path.c
@@ -0,0 +1,559 @@
+/* Copyright (C) 2007-2009 The Android Open Source Project
+**
+** This software is licensed under the terms of the GNU General Public
+** License version 2, as published by the Free Software Foundation, and
+** may be copied, distributed, and modified under those terms.
+**
+** 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.
+*/
+#include "android/utils/path.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#ifdef _WIN32
+#include <process.h>
+#include <shlobj.h>
+#include <tlhelp32.h>
+#include <io.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdint.h>
+#include <limits.h>
+#include <winbase.h>
+#else
+#include <unistd.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <signal.h>
+#endif
+
+#define D(...) ((void)0)
+
+#ifndef CHECKED
+# ifdef _WIN32
+# define CHECKED(ret, call) (ret) = (call)
+# else
+# define CHECKED(ret, call) do { (ret) = (call); } while ((ret) < 0 && errno == EINTR)
+# endif
+#endif
+
+/** PATH HANDLING ROUTINES
+ **
+ ** path_parent() can be used to return the n-level parent of a given directory
+ ** this understands . and .. when encountered in the input path
+ **/
+
+static __inline__ int
+ispathsep(int c)
+{
+#ifdef _WIN32
+ return (c == '/' || c == '\\');
+#else
+ return (c == '/');
+#endif
+}
+
+char*
+path_parent( const char* path, int levels )
+{
+ const char* end = path + strlen(path);
+ char* result;
+
+ while (levels > 0) {
+ const char* base;
+
+ /* trim any trailing path separator */
+ while (end > path && ispathsep(end[-1]))
+ end--;
+
+ base = end;
+ while (base > path && !ispathsep(base[-1]))
+ base--;
+
+ if (base <= path) /* we can't go that far */
+ return NULL;
+
+ if (end == base+1 && base[0] == '.')
+ goto Next;
+
+ if (end == base+2 && base[0] == '.' && base[1] == '.') {
+ levels += 1;
+ goto Next;
+ }
+
+ levels -= 1;
+
+ Next:
+ end = base - 1;
+ }
+ result = malloc( end-path+1 );
+ if (result != NULL) {
+ memcpy( result, path, end-path );
+ result[end-path] = 0;
+ }
+ return result;
+}
+
+static char*
+substring_dup( const char* start, const char* end )
+{
+ int len = end - start;
+ char* result = android_alloc(len+1);
+ memcpy(result, start, len);
+ result[len] = 0;
+ return result;
+}
+
+int
+path_split( const char* path, char* *pdirname, char* *pbasename )
+{
+ const char* end = path + strlen(path);
+ const char* last;
+ char* basename;
+
+ /* prepare for errors */
+ if (pdirname)
+ *pdirname = NULL;
+ if (pbasename)
+ *pbasename = NULL;
+
+ /* handle empty path case */
+ if (end == path) {
+ return -1;
+ }
+
+ /* strip trailing path separators */
+ while (end > path && ispathsep(end[-1]))
+ end -= 1;
+
+ /* handle "/" and degenerate cases like "////" */
+ if (end == path) {
+ return -1;
+ }
+
+ /* find last separator */
+ last = end;
+ while (last > path && !ispathsep(last[-1]))
+ last -= 1;
+
+ /* handle cases where there is no path separator */
+ if (last == path) {
+ if (pdirname)
+ *pdirname = ASTRDUP(".");
+ if (pbasename)
+ *pbasename = substring_dup(path,end);
+ return 0;
+ }
+
+ /* handle "/foo" */
+ if (last == path+1) {
+ if (pdirname)
+ *pdirname = ASTRDUP("/");
+ if (pbasename)
+ *pbasename = substring_dup(path+1,end);
+ return 0;
+ }
+
+ /* compute basename */
+ basename = substring_dup(last,end);
+ if (strcmp(basename, ".") == 0 || strcmp(basename, "..") == 0) {
+ AFREE(basename);
+ return -1;
+ }
+
+ if (pbasename)
+ *pbasename = basename;
+ else {
+ AFREE(basename);
+ }
+
+ /* compute dirname */
+ if (pdirname != NULL)
+ *pdirname = substring_dup(path,last-1);
+
+ return 0;
+}
+
+char*
+path_basename( const char* path )
+{
+ char* basename;
+
+ if (path_split(path, NULL, &basename) < 0)
+ return NULL;
+
+ return basename;
+}
+
+char*
+path_dirname( const char* path )
+{
+ char* dirname;
+
+ if (path_split(path, &dirname, NULL) < 0)
+ return NULL;
+
+ return dirname;
+}
+
+
+
+
+
+/** MISC FILE AND DIRECTORY HANDLING
+ **/
+
+ABool
+path_exists( const char* path )
+{
+ int ret;
+ CHECKED(ret, access(path, F_OK));
+ return (ret == 0) || (errno != ENOENT);
+}
+
+/* checks that a path points to a regular file */
+ABool
+path_is_regular( const char* path )
+{
+ int ret;
+ struct stat st;
+
+ CHECKED(ret, stat(path, &st));
+ if (ret < 0)
+ return 0;
+
+ return S_ISREG(st.st_mode);
+}
+
+
+/* checks that a path points to a directory */
+ABool
+path_is_dir( const char* path )
+{
+ int ret;
+ struct stat st;
+
+ CHECKED(ret, stat(path, &st));
+ if (ret < 0)
+ return 0;
+
+ return S_ISDIR(st.st_mode);
+}
+
+/* checks that one can read/write a given (regular) file */
+ABool
+path_can_read( const char* path )
+{
+ int ret;
+ CHECKED(ret, access(path, R_OK));
+ return (ret == 0);
+}
+
+ABool
+path_can_write( const char* path )
+{
+ int ret;
+ CHECKED(ret, access(path, R_OK));
+ return (ret == 0);
+}
+
+/* try to make a directory. returns 0 on success, -1 on failure
+ * (error code in errno) */
+APosixStatus
+path_mkdir( const char* path, int mode )
+{
+#ifdef _WIN32
+ (void)mode;
+ return _mkdir(path);
+#else
+ int ret;
+ CHECKED(ret, mkdir(path, mode));
+ return ret;
+#endif
+}
+
+static APosixStatus
+path_mkdir_recursive( char* path, unsigned len, int mode )
+{
+ char old_c;
+ int ret;
+ unsigned len2;
+
+ /* get rid of trailing separators */
+ while (len > 0 && ispathsep(path[len-1]))
+ len -= 1;
+
+ if (len == 0) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ /* check that the parent exists, 'len2' is the length of
+ * the parent part of the path */
+ len2 = len-1;
+ while (len2 > 0 && !ispathsep(path[len2-1]))
+ len2 -= 1;
+
+ if (len2 > 0) {
+ old_c = path[len2];
+ path[len2] = 0;
+ ret = 0;
+ if ( !path_exists(path) ) {
+ /* the parent doesn't exist, so try to create it */
+ ret = path_mkdir_recursive( path, len2, mode );
+ }
+ path[len2] = old_c;
+
+ if (ret < 0)
+ return ret;
+ }
+
+ /* at this point, we now the parent exists */
+ old_c = path[len];
+ path[len] = 0;
+ ret = path_mkdir( path, mode );
+ path[len] = old_c;
+
+ return ret;
+}
+
+/* ensure that a given directory exists, create it if not,
+ 0 on success, -1 on failure (error code in errno) */
+APosixStatus
+path_mkdir_if_needed( const char* path, int mode )
+{
+ int ret = 0;
+
+ if (!path_exists(path)) {
+ ret = path_mkdir(path, mode);
+
+ if (ret < 0 && errno == ENOENT) {
+ char temp[MAX_PATH];
+ unsigned len = (unsigned)strlen(path);
+
+ if (len > sizeof(temp)-1) {
+ errno = EINVAL;
+ return -1;
+ }
+ memcpy( temp, path, len );
+ temp[len] = 0;
+
+ return path_mkdir_recursive(temp, len, mode);
+ }
+ }
+ return ret;
+}
+
+/* return the size of a given file in '*psize'. returns 0 on
+ * success, -1 on failure (error code in errno) */
+APosixStatus
+path_get_size( const char* path, uint64_t *psize )
+{
+#ifdef _WIN32
+ /* avoid _stat64 which is only defined in MSVCRT.DLL, not CRTDLL.DLL */
+ /* do not use OpenFile() because it has strange search behaviour that could */
+ /* result in getting the size of a different file */
+ LARGE_INTEGER size;
+ HANDLE file = CreateFile( /* lpFilename */ path,
+ /* dwDesiredAccess */ GENERIC_READ,
+ /* dwSharedMode */ FILE_SHARE_READ|FILE_SHARE_WRITE,
+ /* lpSecurityAttributes */ NULL,
+ /* dwCreationDisposition */ OPEN_EXISTING,
+ /* dwFlagsAndAttributes */ 0,
+ /* hTemplateFile */ NULL );
+ if (file == INVALID_HANDLE_VALUE) {
+ /* ok, just to play fair */
+ errno = ENOENT;
+ return -1;
+ }
+ if (!GetFileSizeEx(file, &size)) {
+ /* maybe we tried to get the size of a pipe or something like that ? */
+ *psize = 0;
+ }
+ else {
+ *psize = (uint64_t) size.QuadPart;
+ }
+ CloseHandle(file);
+ return 0;
+#else
+ int ret;
+ struct stat st;
+
+ CHECKED(ret, stat(path, &st));
+ if (ret == 0) {
+ *psize = (uint64_t) st.st_size;
+ }
+ return ret;
+#endif
+}
+
+
+ABool
+path_is_absolute( const char* path )
+{
+#ifdef _WIN32
+ if (path == NULL)
+ return 0;
+
+ if (path[0] == '/' || path[0] == '\\')
+ return 1;
+
+ /* 'C:' is always considered to be absolute
+ * even if used with a relative path like C:foo which
+ * is different from C:\foo
+ */
+ if (path[0] != 0 && path[1] == ':')
+ return 1;
+
+ return 0;
+#else
+ return (path != NULL && path[0] == '/');
+#endif
+}
+
+
+/** OTHER FILE UTILITIES
+ **
+ ** path_empty_file() creates an empty file at a given path location.
+ ** if the file already exists, it is truncated without warning
+ **
+ ** path_copy_file() copies one file into another.
+ **
+ ** both functions return 0 on success, and -1 on error
+ **/
+
+APosixStatus
+path_empty_file( const char* path )
+{
+#ifdef _WIN32
+ int fd = _creat( path, S_IWRITE );
+#else
+ /* on Unix, only allow the owner to read/write, since the file *
+ * may contain some personal data we don't want to see exposed */
+ int fd = creat(path, S_IRUSR | S_IWUSR);
+#endif
+ if (fd >= 0) {
+ close(fd);
+ return 0;
+ }
+ return -1;
+}
+
+APosixStatus
+path_copy_file( const char* dest, const char* source )
+{
+ int fd, fs, result = -1;
+
+ /* if the destination doesn't exist, create it */
+ if ( access(source, F_OK) < 0 ||
+ path_empty_file(dest) < 0) {
+ return -1;
+ }
+
+#ifdef _WIN32
+ fd = _open(dest, _O_RDWR | _O_BINARY);
+ fs = _open(source, _O_RDONLY | _O_BINARY);
+#else
+ fd = creat(dest, S_IRUSR | S_IWUSR);
+ fs = open(source, S_IREAD);
+#endif
+ if (fs >= 0 && fd >= 0) {
+ char buf[4096];
+ ssize_t total = 0;
+ ssize_t n;
+ result = 0; /* success */
+ while ((n = read(fs, buf, 4096)) > 0) {
+ if (write(fd, buf, n) != n) {
+ /* write failed. Make it return -1 so that an
+ * empty file be created. */
+ D("Failed to copy '%s' to '%s': %s (%d)",
+ source, dest, strerror(errno), errno);
+ result = -1;
+ break;
+ }
+ total += n;
+ }
+ }
+
+ if (fs >= 0) {
+ close(fs);
+ }
+ if (fd >= 0) {
+ close(fd);
+ }
+ return result;
+}
+
+
+APosixStatus
+path_delete_file( const char* path )
+{
+#ifdef _WIN32
+ int ret = _unlink( path );
+ if (ret == -1 && errno == EACCES) {
+ /* a first call to _unlink will fail if the file is set read-only */
+ /* we can however try to change its mode first and call unlink */
+ /* again... */
+ ret = _chmod( path, _S_IREAD | _S_IWRITE );
+ if (ret == 0)
+ ret = _unlink( path );
+ }
+ return ret;
+#else
+ return unlink(path);
+#endif
+}
+
+
+void*
+path_load_file(const char *fn, size_t *pSize)
+{
+ char* data;
+ int sz;
+ int fd;
+
+ if (pSize)
+ *pSize = 0;
+
+ data = NULL;
+
+ fd = open(fn, O_BINARY | O_RDONLY);
+ if(fd < 0) return NULL;
+
+ do {
+ sz = lseek(fd, 0, SEEK_END);
+ if(sz < 0) break;
+
+ if (pSize)
+ *pSize = (size_t) sz;
+
+ if (lseek(fd, 0, SEEK_SET) != 0)
+ break;
+
+ data = (char*) malloc(sz + 1);
+ if(data == NULL) break;
+
+ if (read(fd, data, sz) != sz)
+ break;
+
+ close(fd);
+ data[sz] = 0;
+
+ return data;
+ } while (0);
+
+ close(fd);
+
+ if(data != NULL)
+ free(data);
+
+ return NULL;
+}
+