rizin/librz/util/file.c
2021-01-03 11:42:38 +01:00

1237 lines
No EOL
26 KiB
C

// SPDX-License-Identifier: LGPL-3.0-only
#include "rz_types.h"
#include "rz_util.h"
#include <stdio.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <rz_lib.h>
#if __UNIX__
#include <sys/time.h>
#include <sys/mman.h>
#include <limits.h>
#endif
#if __APPLE__ && __MAC_10_5
#define HAVE_COPYFILE_H 1
#else
#define HAVE_COPYFILE_H 0
#endif
#if HAVE_COPYFILE_H
#include <copyfile.h>
#endif
#if _MSC_VER
#include <process.h>
#endif
#define BS 1024
static int file_stat (const char *file, struct stat* const pStat) {
rz_return_val_if_fail (file && pStat, -1);
#if __WINDOWS__
wchar_t *wfile = rz_utf8_to_utf16 (file);
if (!wfile) {
return -1;
}
int ret = _wstat (wfile, pStat);
free (wfile);
return ret;
#else // __WINDOWS__
return stat (file, pStat);
#endif // __WINDOWS__
}
RZ_API bool rz_file_truncate(const char *filename, ut64 newsize) {
rz_return_val_if_fail (filename, false);
int fd;
if (rz_file_is_directory (filename)) {
return false;
}
if (!rz_file_exists (filename) || !rz_file_is_regular (filename)) {
return false;
}
#if __WINDOWS__
fd = rz_sys_open (filename, O_RDWR, 0644);
#else
fd = rz_sys_open (filename, O_RDWR | O_SYNC, 0644);
#endif
if (fd == -1) {
return false;
}
#ifdef _MSC_VER
int r = _chsize (fd, newsize);
#else
int r = ftruncate (fd, newsize);
#endif
if (r != 0) {
eprintf ("Could not resize %s file\n", filename);
close (fd);
return false;
}
close (fd);
return true;
}
/*
Example:
str = rz_file_basename ("home/inisider/Downloads/user32.dll");
// str == user32.dll
*/
RZ_API const char *rz_file_basename(const char *path) {
rz_return_val_if_fail (path, NULL);
const char *ptr = rz_str_rchr (path, NULL, '/');
if (ptr) {
path = ptr + 1;
} else {
if ((ptr = rz_str_rchr (path, NULL, '\\'))) {
path = ptr + 1;
}
}
return path;
}
/*
Example:
str = rz_file_dirname ("home/inisider/Downloads/user32.dll");
// str == "home/inisider/Downloads"
free (str);
*/
RZ_API char *rz_file_dirname(const char *path) {
rz_return_val_if_fail (path, NULL);
char *newpath = strdup (path);
char *ptr = (char*)rz_str_rchr (newpath, NULL, '/');
if (ptr) {
if (ptr == newpath) {
ptr++;
}
*ptr = 0;
} else {
ptr = (char*)rz_str_rchr (newpath, NULL, '\\');
if (!ptr) {
ptr = newpath;
}
if (ptr && ptr == newpath && *ptr == '.') { // keep '.'
ptr++;
if (*ptr == '.') { // keep '..'
ptr++;
}
}
*ptr = 0;
}
return newpath;
}
RZ_API bool rz_file_is_c(const char *file) {
rz_return_val_if_fail (file, false);
const char *ext = rz_str_lchr (file, '.'); // TODO: add api in rz_file_extension or rz_str_ext for this
if (ext) {
ext++;
if (!strcmp (ext, "cparse")
|| !strcmp (ext, "c")
|| !strcmp (ext, "h")) {
return true;
}
}
return false;
}
RZ_API bool rz_file_is_regular(const char *str) {
struct stat buf = {0};
if (!str || !*str || file_stat (str, &buf) == -1) {
return false;
}
return ((S_IFREG & buf.st_mode) == S_IFREG);
}
RZ_API bool rz_file_is_directory(const char *str) {
struct stat buf = {0};
rz_return_val_if_fail (!RZ_STR_ISEMPTY (str), false);
if (file_stat (str, &buf) == -1) {
return false;
}
#ifdef S_IFBLK
if ((S_IFBLK & buf.st_mode) == S_IFBLK) {
return false;
}
#endif
return S_IFDIR == (S_IFDIR & buf.st_mode);
}
RZ_API bool rz_file_fexists(const char *fmt, ...) {
int ret;
char string[BS];
va_list ap;
va_start (ap, fmt);
vsnprintf (string, sizeof (string), fmt, ap);
ret = rz_file_exists (string);
va_end (ap);
return ret;
}
RZ_API bool rz_file_exists(const char *str) {
char *absfile = rz_file_abspath (str);
struct stat buf = {0};
rz_return_val_if_fail (!RZ_STR_ISEMPTY (str), false);
if (file_stat (absfile, &buf) == -1) {
free (absfile);
return false;
}
free (absfile);
return S_IFREG == (S_IFREG & buf.st_mode);
}
RZ_API ut64 rz_file_size(const char *str) {
rz_return_val_if_fail (!RZ_STR_ISEMPTY (str), 0);
struct stat buf = {0};
if (file_stat (str, &buf) == -1) {
return 0;
}
return (ut64)buf.st_size;
}
RZ_API bool rz_file_is_abspath(const char *file) {
rz_return_val_if_fail (!RZ_STR_ISEMPTY (file), 0);
return ((*file && file[1]==':') || *file == '/');
}
RZ_API char *rz_file_abspath_rel(const char *cwd, const char *file) {
char *ret = NULL;
if (!file || !strcmp (file, ".") || !strcmp (file, "./")) {
return strdup (cwd);
}
if (strstr (file, "://")) {
return strdup (file);
}
if (!strncmp (file, "~/", 2) || !strncmp (file, "~\\", 2)) {
ret = rz_str_home (file + 2);
} else {
#if __UNIX__
if (cwd && *file != '/') {
ret = rz_str_newf ("%s" RZ_SYS_DIR "%s", cwd, file);
}
#elif __WINDOWS__
// Network path
if (!strncmp (file, "\\\\", 2)) {
return strdup (file);
}
if (!strchr (file, ':')) {
ret = rz_str_newf ("%s" RZ_SYS_DIR "%s", cwd, file);
}
#endif
}
if (!ret) {
ret = strdup (file);
}
#if __UNIX__
char *abspath = realpath (ret, NULL);
if (abspath) {
free (ret);
ret = abspath;
}
#endif
return ret;
}
RZ_API char *rz_file_abspath(const char *file) {
rz_return_val_if_fail (file, NULL);
char *cwd = rz_sys_getdir ();
if (cwd) {
char *ret = rz_file_abspath_rel (cwd, file);
free (cwd);
return ret;
}
return NULL;
}
RZ_API char *rz_file_relpath(const char *base, const char *path) {
// skip longest common prefix
while (*base && *path) {
while (*base == *RZ_SYS_DIR) {
base++;
}
while (*path == *RZ_SYS_DIR) {
path++;
}
while (*base && *path && *base != *RZ_SYS_DIR && *path != *RZ_SYS_DIR) {
if (*base != *path) {
goto diverge;
}
base++;
path++;
}
}
while (*path == *RZ_SYS_DIR) {
path++;
}
size_t ups;
diverge:
// count number of ".." needed which is just the number of remaining tokens in base
ups = 0;
while (*base) {
while (*base == *RZ_SYS_DIR) {
base++;
}
if (!*base) {
break;
}
ups++;
while (*base && *base != *RZ_SYS_DIR) {
base++;
}
}
// put all the ".."s and append the rest of the path
size_t suff_len = strlen (path);
char *r = malloc (ups * 3 + suff_len + 1); // ups * strlen("../") + strlen(path)
if (!r) {
return NULL;
}
size_t i;
for (i = 0; i < ups; i++) {
r[i * 3] = '.';
r[i * 3 + 1] = '.';
r[i * 3 + 2] = *RZ_SYS_DIR;
}
memcpy (r + i * 3, path, suff_len + 1);
return r;
}
RZ_API char *rz_file_path_local_to_unix(const char *path) {
char *r = strdup (path);
if (!r) {
return NULL;
}
#if __WINDOWS__
rz_str_replace (r, RZ_SYS_DIR, "/", true);
#endif
return r;
}
RZ_API char *rz_file_path_unix_to_local(const char *path) {
char *r = strdup (path);
if (!r) {
return NULL;
}
#if __WINDOWS__
rz_str_replace (r, "/", RZ_SYS_DIR, true);
#endif
return r;
}
RZ_API char *rz_file_path(const char *bin) {
rz_return_val_if_fail (bin, NULL);
char *file = NULL;
char *path = NULL;
char *str, *ptr;
const char *extension = "";
if (!strncmp (bin, "./", 2)) {
return rz_file_exists (bin)
? rz_file_abspath (bin): NULL;
}
char *path_env = (char *)rz_sys_getenv ("PATH");
#if __WINDOWS__
if (!rz_str_endswith (bin, ".exe")) {
extension = ".exe";
}
#endif
if (path_env) {
str = path = strdup (path_env);
do {
ptr = strchr (str, RZ_SYS_ENVSEP[0]);
if (ptr) {
*ptr = '\0';
file = rz_str_newf (RZ_JOIN_2_PATHS ("%s", "%s%s"), str, bin, extension);
if (rz_file_exists (file)) {
free (path);
free (path_env);
return file;
}
str = ptr + 1;
free (file);
}
} while (ptr);
}
free (path_env);
free (path);
return strdup (bin);
}
RZ_API char *rz_stdin_slurp (int *sz) {
#if __UNIX__ || __WINDOWS__
int i, ret, newfd;
if ((newfd = dup (0)) < 0) {
return NULL;
}
char *buf = malloc (BS);
if (!buf) {
close (newfd);
return NULL;
}
for (i = ret = 0; i >= 0; i += ret) {
char *new = realloc (buf, i + BS);
if (!new) {
eprintf ("Cannot realloc to %d\n", i+BS);
free (buf);
return NULL;
}
buf = new;
ret = read (0, buf + i, BS);
if (ret < 1) {
break;
}
}
if (i < 1) {
i = 0;
RZ_FREE (buf);
} else {
buf[i] = 0;
dup2 (newfd, 0);
close (newfd);
}
if (sz) {
*sz = i;
}
if (!i) {
RZ_FREE (buf);
}
return buf;
#else
#warning TODO rz_stdin_slurp
return NULL;
#endif
}
RZ_API char *rz_file_slurp(const char *str, RZ_NULLABLE size_t *usz) {
rz_return_val_if_fail (str, NULL);
if (usz) {
*usz = 0;
}
if (!rz_file_exists (str)) {
return NULL;
}
FILE *fd = rz_sys_fopen (str, "rb");
if (!fd) {
return NULL;
}
if (fseek (fd, 0, SEEK_END) == -1) {
// cannot determine the size of the file
}
long sz = ftell (fd);
if (sz < 0) {
fclose (fd);
return NULL;
}
if (!sz) {
if (rz_file_is_regular (str)) {
char *buf = NULL;
long size = 0;
(void)fseek (fd, 0, SEEK_SET);
do {
char *nbuf = realloc (buf, size + BS);
if (!nbuf) {
break;
}
buf = nbuf;
size_t r = fread (buf + size, 1, BS, fd);
if (ferror (fd)) {
RZ_FREE (buf);
goto regular_err;
}
size += r;
} while (!feof (fd));
char *nbuf = realloc (buf, size + 1);
if (!nbuf) {
free (buf);
return NULL;
}
buf = nbuf;
buf[size] = '\0';
if (usz) {
*usz = size;
}
regular_err:
fclose (fd);
return buf;
}
// try to read 64K
sz = UT16_MAX;
}
rewind (fd);
char *ret = (char *)malloc (sz + 1);
if (!ret) {
fclose (fd);
return NULL;
}
size_t rsz = fread (ret, 1, sz, fd);
if (rsz != sz) {
eprintf ("Warning: rz_file_slurp: fread: truncated read\n");
sz = rsz;
}
fclose (fd);
ret[sz] = '\0';
if (usz) {
*usz = sz;
}
return ret;
}
RZ_API ut8 *rz_file_gzslurp(const char *str, int *outlen, int origonfail) {
rz_return_val_if_fail (str, NULL);
if (outlen) {
*outlen = 0;
}
size_t sz;
ut8 *in = (ut8*)rz_file_slurp (str, &sz);
if (!in) {
return NULL;
}
ut8 *out = rz_inflate (in, (int)sz, NULL, outlen);
if (!out && origonfail) {
// if uncompression fails, return orig buffer ?
if (outlen) {
*outlen = (int)sz;
}
in[sz] = 0;
return in;
}
free (in);
return out;
}
RZ_API ut8 *rz_file_slurp_hexpairs(const char *str, int *usz) {
rz_return_val_if_fail (str, NULL);
if (usz) {
*usz = 0;
}
ut8 *ret;
long sz;
int c, bytes = 0;
FILE *fd = rz_sys_fopen (str, "rb");
if (!fd) {
return NULL;
}
(void) fseek (fd, 0, SEEK_END);
sz = ftell (fd);
(void) fseek (fd, 0, SEEK_SET);
ret = (ut8*)malloc ((sz>>1)+1);
if (!ret) {
fclose (fd);
return NULL;
}
for (;;) {
if (fscanf (fd, " #%*[^\n]") == 1) {
continue;
}
if (fscanf (fd, "%02x", &c) == 1) {
ret[bytes++] = c;
continue;
}
if (feof (fd)) {
break;
}
free (ret);
fclose (fd);
return NULL;
}
ret[bytes] = '\0';
fclose (fd);
if (usz) {
*usz = bytes;
}
return ret;
}
RZ_API char *rz_file_slurp_range(const char *str, ut64 off, int sz, int *osz) {
char *ret;
size_t read_items;
FILE *fd = rz_sys_fopen (str, "rb");
if (!fd) {
return NULL;
}
// XXX handle out of bound reads (eof)
if (fseek (fd, off, SEEK_SET) < 0) {
fclose (fd);
return NULL;
}
ret = (char *) malloc (sz + 1);
if (ret) {
if (osz) {
*osz = (int)(size_t) fread (ret, 1, sz, fd);
} else {
read_items = fread (ret, 1, sz, fd);
if (!read_items) {
fclose (fd);
return ret;
}
}
ret[sz] = '\0';
}
fclose (fd);
return ret;
}
RZ_API char *rz_file_slurp_random_line(const char *file) {
rz_return_val_if_fail (file, NULL);
int i = 0;
return rz_file_slurp_random_line_count (file, &i);
}
RZ_API char *rz_file_slurp_random_line_count(const char *file, int *line) {
rz_return_val_if_fail (file && line, NULL);
/* Reservoir Sampling */
char *ptr = NULL, *str;
size_t i, lines, selection = -1;
int start = *line;
if ((str = rz_file_slurp (file, NULL))) {
rz_num_irand ();
for (i = 0; str[i]; i++) {
if (str[i] == '\n') {
//here rand doesn't have any security implication
// https://www.securecoding.cert.org/confluence/display/c/MSC30-C.+Do+not+use+the+rand()+function+for+generating+pseudorandom+numbers
if (!(rz_num_rand ((++(*line))))) {
selection = (*line - 1); /* The line we want. */
}
}
}
if ((selection < start) || (selection == -1)) {
free (str);
return NULL;
} else {
lines = selection - start;
}
if (lines > 0) {
for (i = 0; str[i] && lines; i++) {
if (str[i] == '\n') {
lines--;
}
}
ptr = str + i;
for (i = 0; ptr[i]; i++) {
if (ptr[i] == '\n') {
ptr[i] = '\0';
break;
}
}
ptr = strdup (ptr);
}
free (str);
}
return ptr;
}
RZ_API char *rz_file_slurp_line(const char *file, int line, int context) {
rz_return_val_if_fail (file, NULL);
int i, lines = 0;
size_t sz;
char *ptr = NULL, *str = rz_file_slurp (file, &sz);
// TODO: Implement context
if (str) {
for (i = 0; str[i]; i++) {
if (str[i] == '\n') {
lines++;
}
}
if (line > lines) {
free (str);
return NULL;
}
lines = line - 1;
for (i = 0; str[i]&&lines; i++) {
if (str[i] == '\n') {
lines--;
}
}
ptr = str+i;
for (i = 0; ptr[i]; i++) {
if (ptr[i] == '\n') {
ptr[i] = '\0';
break;
}
}
ptr = strdup (ptr);
free (str);
}
return ptr;
}
RZ_API char *rz_file_slurp_lines_from_bottom(const char *file, int line) {
rz_return_val_if_fail (file, NULL);
int i, lines = 0;
size_t sz;
char *ptr = NULL, *str = rz_file_slurp (file, &sz);
// TODO: Implement context
if (str) {
for (i = 0; str[i]; i++) {
if (str[i] == '\n') {
lines++;
}
}
if (line > lines) {
return strdup (str); // number of lines requested in more than present, return all
}
i--;
for (; str[i] && line; i--) {
if (str[i] == '\n') {
line--;
}
}
ptr = str+i;
ptr = strdup (ptr);
free (str);
}
return ptr;
}
RZ_API char *rz_file_slurp_lines(const char *file, int line, int count) {
rz_return_val_if_fail (file, NULL);
int i, lines = 0;
size_t sz;
char *ptr = NULL, *str = rz_file_slurp (file, &sz);
// TODO: Implement context
if (str) {
for (i = 0; str[i]; i++) {
if (str[i] == '\n') {
lines++;
}
}
if (line > lines) {
free (str);
return NULL;
}
lines = line - 1;
for (i = 0; str[i]&&lines; i++) {
if (str[i] == '\n') {
lines--;
}
}
ptr = str+i;
for (i = 0; ptr[i]; i++) {
if (ptr[i] == '\n') {
if (count) {
count--;
} else {
ptr[i] = '\0';
break;
}
}
}
ptr = strdup (ptr);
free (str);
}
return ptr;
}
RZ_API char *rz_file_root(const char *root, const char *path) {
rz_return_val_if_fail (root && path, NULL);
char *ret, *s = rz_str_replace (strdup (path), "..", "", 1);
// XXX ugly hack
while (strstr (s, "..")) {
s = rz_str_replace (s, "..", "", 1);
}
while (strstr (s, "./")) {
s = rz_str_replace (s, "./", "", 1);
}
while (strstr (s, "//")) {
s = rz_str_replace (s, "//", "", 1);
}
ret = rz_str_append (strdup (root), RZ_SYS_DIR);
ret = rz_str_append (ret, s);
free (s);
return ret;
}
RZ_API bool rz_file_hexdump(const char *file, const ut8 *buf, int len, int append) {
FILE *fd;
int i,j;
if (!file || !*file || !buf || len < 0) {
eprintf ("rz_file_hexdump file: %s buf: %p\n", file, buf);
return false;
}
if (append) {
fd = rz_sys_fopen (file, "ab");
} else {
rz_sys_truncate (file, 0);
fd = rz_sys_fopen (file, "wb");
}
if (!fd) {
eprintf ("Cannot open '%s' for writing\n", file);
return false;
}
for (i = 0; i < len; i += 16) {
int l = RZ_MIN (16, len - i);
fprintf (fd, "0x%08"PFMT64x" ", (ut64)i);
for (j = 0; j + 2 <= l; j += 2) {
fprintf (fd, "%02x%02x ", buf[i +j], buf[i+j+1]);
}
if (j < l) {
fprintf (fd, "%02x ", buf[i + j]);
j += 2;
}
if (j < 16) {
fprintf (fd, "%*s ", (16 - j) / 2 * 5, "");
}
for (j = 0; j < 16; j++) {
fprintf (fd, "%c", j < l && IS_PRINTABLE (buf[i + j])? buf[i+j]: '.');
}
fprintf (fd, "\n");
}
fclose (fd);
return true;
}
RZ_API bool rz_file_touch(const char *file) {
rz_return_val_if_fail (file, false);
return rz_file_dump (file, NULL, 0, true);
}
RZ_API bool rz_file_dump(const char *file, const ut8 *buf, int len, bool append) {
rz_return_val_if_fail (!RZ_STR_ISEMPTY (file), false);
FILE *fd;
if (append) {
fd = rz_sys_fopen (file, "ab");
} else {
rz_sys_truncate (file, 0);
fd = rz_sys_fopen (file, "wb");
}
if (!fd) {
eprintf ("Cannot open '%s' for writing\n", file);
return false;
}
if (buf) {
if (len < 0) {
len = strlen ((const char *)buf);
}
if (len > 0 && fwrite (buf, len, 1, fd) != 1) {
rz_sys_perror ("rz_file_dump: fwrite: error\n");
fclose (fd);
return false;
}
}
fclose (fd);
return true;
}
RZ_API bool rz_file_rm(const char *file) {
rz_return_val_if_fail (!RZ_STR_ISEMPTY (file), false);
if (rz_file_is_directory (file)) {
#if __WINDOWS__
LPTSTR file_ = rz_sys_conv_utf8_to_win (file);
bool ret = RemoveDirectory (file_);
free (file_);
return !ret;
#else
return !rmdir (file);
#endif
} else {
#if __WINDOWS__
LPTSTR file_ = rz_sys_conv_utf8_to_win (file);
bool ret = DeleteFile (file_);
free (file_);
return !ret;
#else
return !unlink (file);
#endif
}
}
RZ_API char *rz_file_readlink(const char *path) {
rz_return_val_if_fail (!RZ_STR_ISEMPTY (path), false);
#if __UNIX__
int ret;
char pathbuf[4096] = {0};
strncpy (pathbuf, path, sizeof (pathbuf) - 1);
repeat:
ret = readlink (path, pathbuf, sizeof (pathbuf)-1);
if (ret != -1) {
pathbuf[ret] = 0;
path = pathbuf;
goto repeat;
}
return strdup (pathbuf);
#endif
return NULL;
}
#if __WINDOWS__
static RzMmap *file_mmap(RzMmap *m) {
LPTSTR file_ = rz_sys_conv_utf8_to_win (m->filename);
bool is_write = (m->perm & O_WRONLY) || (m->perm & O_RDWR);
bool is_creat = m->perm & O_CREAT;
HANDLE fh = (HANDLE)_get_osfhandle (m->fd);
m->len = (DWORD)GetFileSize (fh, (LPDWORD)((char *)&m->len + sizeof (DWORD)));
if (m->len == INVALID_FILE_SIZE) {
rz_sys_perror ("GetFileSize");
goto err;
}
if (m->len != 0) {
m->fm = CreateFileMapping (fh,
NULL,
is_write? PAGE_READWRITE: PAGE_READONLY,
0, 0, NULL);
if (!m->fm) {
rz_sys_perror ("CreateFileMapping mmap");
goto err;
}
m->buf = MapViewOfFileEx (m->fm,
is_write? (FILE_MAP_READ|FILE_MAP_WRITE): FILE_MAP_READ,
0, 0, 0, (void *)m->base);
if (!m->buf) {
rz_sys_perror ("MapViewOfFileEx");
goto err;
}
}
return m;
err:
rz_file_mmap_free (m);
free (file_);
return NULL;
}
#elif __UNIX__
static RzMmap *file_mmap(RzMmap *m) {
m->len = lseek (m->fd, (off_t)0, SEEK_END);
if (m->len) {
bool is_write = (m->perm & O_WRONLY) || (m->perm & O_RDWR);
m->buf = mmap ((void *)m->base,
m->len,
is_write? PROT_READ | PROT_WRITE: PROT_READ,
MAP_SHARED, m->fd, 0);
if (m->buf == MAP_FAILED) {
rz_sys_perror ("mmap");
rz_file_mmap_free (m);
return NULL;
}
}
return m;
}
#else
static RzMmap *file_mmap(RzMmap *m) {
m->len = lseek (m->fd, (off_t)0, SEEK_END);
m->buf = malloc (m->len));
if (!m->buf) {
rz_file_mmap_free (m);
return NULL;
}
lseek (m->fd, (off_t)0, SEEK_SET);
read (m->fd, m->buf, m->len);
return m;
}
#endif
RZ_API RzMmap *rz_file_mmap(const char *file, int perm, int mode, ut64 base) {
RzMmap *m = NULL;
m = RZ_NEW0 (RzMmap);
if (!m) {
return NULL;
}
m->base = base;
m->perm = perm;
m->len = 0;
m->filename = strdup (file);
m->mode = mode;
if (!m->filename) {
rz_file_mmap_free (m);
return NULL;
}
m->fd = rz_sys_open (m->filename, m->perm, m->mode);
if (m->fd == -1) {
rz_file_mmap_free (m);
return NULL;
}
return file_mmap (m);
}
RZ_API void rz_file_mmap_free(RzMmap *m) {
if (!m) {
return;
}
#if __WINDOWS__
if (m->buf) {
UnmapViewOfFile (m->buf);
}
if (m->fm) {
CloseHandle (m->fm);
}
if (m->fd != -1) {
_close (m->fd);
}
#elif __UNIX__
munmap (m->buf, m->len);
close (m->fd);
#endif
free (m->filename);
free (m);
}
RZ_API void *rz_file_mmap_resize(RzMmap *m, ut64 newsize) {
#if __WINDOWS__
if (m->buf) {
UnmapViewOfFile (m->buf);
}
if (m->fm) {
CloseHandle (m->fm);
}
if (m->fd != -1) {
_close (m->fd);
}
#elif __UNIX__
if (m->buf && munmap (m->buf, m->len) != 0) {
return NULL;
}
#endif
if (!rz_sys_truncate (m->filename, newsize)) {
return NULL;
}
m->fd = rz_sys_open (m->filename, m->perm, m->mode);
if (m->fd == -1) {
rz_file_mmap_free (m);
return NULL;
}
file_mmap (m);
return m->buf;
}
RZ_API char *rz_file_temp (const char *prefix) {
if (!prefix) {
prefix = "";
}
char *path = rz_file_tmpdir ();
char *res = rz_str_newf ("%s" RZ_SYS_DIR "%s.%"PFMT64x, path, prefix, rz_time_now ());
free (path);
return res;
}
RZ_API int rz_file_mkstemp(RZ_NULLABLE const char *prefix, char **oname) {
int h = -1;
char *path = rz_file_tmpdir ();
if (!prefix) {
prefix = "rz";
}
#if __WINDOWS__
LPTSTR name = NULL;
LPTSTR path_ = rz_sys_conv_utf8_to_win (path);
LPTSTR prefix_ = rz_sys_conv_utf8_to_win (prefix);
name = (LPTSTR)malloc (sizeof (TCHAR) * (MAX_PATH + 1));
if (!name) {
goto err_r_file_mkstemp;
}
if (GetTempFileName (path_, prefix_, 0, name)) {
char *name_ = rz_sys_conv_win_to_utf8 (name);
h = rz_sys_open (name_, O_RDWR|O_EXCL|O_BINARY, 0644);
if (oname) {
if (h != -1) {
*oname = name_;
} else {
*oname = NULL;
free (name_);
}
} else {
free (name_);
}
}
err_r_file_mkstemp:
free (name);
free (path_);
free (prefix_);
#else
char pfxx[1024];
const char *suffix = strchr (prefix, '*');
if (suffix) {
suffix++;
rz_str_ncpy (pfxx, prefix, (size_t)(suffix - prefix));
prefix = pfxx;
} else {
suffix = "";
}
char *name = rz_str_newf ("%s/rz.%s.XXXXXX%s", path, prefix, suffix);
mode_t mask = umask (S_IWGRP | S_IWOTH);
if (suffix && *suffix) {
#if defined(__GLIBC__) && defined(__GLIBC_MINOR__) && 2 <= __GLIBC__ && 19 <= __GLIBC__MINOR__
h = mkstemps (name, strlen (suffix));
#else
char *const xpos = strrchr (name, 'X');
const char c = (char)(NULL != xpos ? *(xpos + 1) : 0);
if (0 != c) {
xpos[1] = 0;
h = mkstemp (name);
xpos[1] = c;
} else {
h = -1;
}
#endif
} else {
h = mkstemp (name);
}
umask (mask);
if (oname) {
*oname = (h!=-1)? strdup (name): NULL;
}
free (name);
#endif
free (path);
return h;
}
RZ_API char *rz_file_tmpdir(void) {
#if __WINDOWS__
LPTSTR tmpdir;
char *path = NULL;
DWORD len = 0;
tmpdir = (LPTSTR)calloc (1, sizeof (TCHAR) * (MAX_PATH + 1));
if (!tmpdir) {
return NULL;
}
if ((len = GetTempPath (MAX_PATH + 1, tmpdir)) == 0) {
path = rz_sys_getenv ("TEMP");
if (!path) {
path = strdup ("C:\\WINDOWS\\Temp\\");
}
} else {
tmpdir[len] = 0;
DWORD (WINAPI *glpn)(LPCTSTR, LPCTSTR, DWORD) = rz_lib_dl_sym (GetModuleHandle (TEXT ("kernel32.dll")), W32_TCALL("GetLongPathName"));
if (glpn) {
// Windows XP sometimes returns short path name
glpn (tmpdir, tmpdir, MAX_PATH + 1);
}
path = rz_sys_conv_win_to_utf8 (tmpdir);
}
free (tmpdir);
// Windows 7, stat() function fail if tmpdir ends with '\\'
if (path) {
size_t path_len = strlen (path);
if (path_len > 0 && path[path_len - 1] == '\\') {
path[path_len - 1] = '\0';
}
}
#else
char *path = rz_sys_getenv ("TMPDIR");
if (path && !*path) {
RZ_FREE (path);
}
if (!path) {
#if __ANDROID__
path = strdup ("/data/data/org.rizin.rizininstaller/rizin/tmp");
#else
path = strdup ("/tmp");
#endif
}
#endif
if (!rz_file_is_directory (path)) {
eprintf ("Cannot find temporary directory '%s'\n", path);
}
return path;
}
RZ_API bool rz_file_copy (const char *src, const char *dst) {
/* TODO: implement in C */
/* TODO: Use NO_CACHE for iOS dyldcache copying */
#if HAVE_COPYFILE_H
return copyfile (src, dst, 0, COPYFILE_DATA | COPYFILE_XATTR) != -1;
#elif __WINDOWS__
PTCHAR s = rz_sys_conv_utf8_to_win (src);
PTCHAR d = rz_sys_conv_utf8_to_win (dst);
if (!s || !d) {
RZ_LOG_ERROR ("rz_file_copy: Failed to allocate memory\n");
free (s);
free (d);
return false;
}
bool ret = CopyFile (s, d, 0);
if (!ret) {
rz_sys_perror ("rz_file_copy");
}
free (s);
free (d);
return ret;
#else
char *src2 = rz_str_replace (strdup (src), "'", "\\'", 1);
char *dst2 = rz_str_replace (strdup (dst), "'", "\\'", 1);
int rc = rz_sys_cmdf ("cp -f '%s' '%s'", src2, dst2);
free (src2);
free (dst2);
return rc == 0;
#endif
}
static void recursive_search_glob (const char *path, const char *glob, RzList* list, int depth) {
if (depth < 1) {
return;
}
char* file;
RzListIter *iter;
RzList *dir = rz_sys_dir (path);
rz_list_foreach (dir, iter, file) {
if (!strcmp (file, ".") || !strcmp (file, "..")) {
continue;
}
char *filename = malloc (strlen (path) + strlen (file) + 2);
strcpy (filename, path);
strcat (filename, file);
if (rz_file_is_directory (filename)) {
strcat (filename, RZ_SYS_DIR);
recursive_search_glob (filename, glob, list, depth - 1);
free (filename);
} else if (rz_str_glob (file, glob)) {
rz_list_append (list, filename);
} else {
free (filename);
}
}
rz_list_free (dir);
}
RZ_API RzList* rz_file_globsearch (const char *_globbed_path, int maxdepth) {
char *globbed_path = strdup (_globbed_path);
RzList *files = rz_list_newf (free);
char *glob = strchr (globbed_path, '*');
if (!glob) {
rz_list_append (files, strdup (globbed_path));
} else {
*glob = '\0';
char *last_slash = (char *)rz_str_last (globbed_path, RZ_SYS_DIR);
*glob = '*';
char *path, *glob_ptr;
if (last_slash) {
glob_ptr = last_slash + 1;
if (globbed_path[0] == '~') {
char *rpath = rz_str_newlen (globbed_path + 2, last_slash - globbed_path - 1);
path = rz_str_home (rpath ? rpath : "");
free (rpath);
} else {
path = rz_str_newlen (globbed_path, last_slash - globbed_path + 1);
}
} else {
glob_ptr = globbed_path;
path = rz_str_newf (".%s", RZ_SYS_DIR);
}
if (!path) {
rz_list_free (files);
free (globbed_path);
return NULL;
}
if (*(glob + 1) == '*') { // "**"
recursive_search_glob (path, glob_ptr, files, maxdepth);
} else { // "*"
recursive_search_glob (path, glob_ptr, files, 1);
}
free (path);
}
free (globbed_path);
return files;
}
RZ_API char *rz_file_path_join(const char *s1, const char *s2) {
bool ends_with_dir = s1[strlen (s1) - 1] == RZ_SYS_DIR[0];
const char *sep = ends_with_dir? "": RZ_SYS_DIR;
return rz_str_newf ("%s%s%s", s1, sep, s2);
}