yolobs-studio/libobs/util/platform.c

605 lines
11 KiB
C
Raw Normal View History

2016-02-23 23:16:51 +00:00
/*
* Copyright (c) 2013-2014 Hugh Bailey <obs.jim@gmail.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#define _FILE_OFFSET_BITS 64
#include <errno.h>
#include <stdlib.h>
#include <locale.h>
#include "c99defs.h"
#include "platform.h"
#include "bmem.h"
#include "utf8.h"
#include "dstr.h"
FILE *os_wfopen(const wchar_t *path, const char *mode)
{
FILE *file = NULL;
if (path) {
#ifdef _MSC_VER
wchar_t *wcs_mode;
os_utf8_to_wcs_ptr(mode, 0, &wcs_mode);
file = _wfopen(path, wcs_mode);
bfree(wcs_mode);
#else
char *mbs_path;
os_wcs_to_utf8_ptr(path, 0, &mbs_path);
file = fopen(mbs_path, mode);
bfree(mbs_path);
#endif
}
return file;
}
FILE *os_fopen(const char *path, const char *mode)
{
#ifdef _WIN32
wchar_t *wpath = NULL;
FILE *file = NULL;
if (path) {
os_utf8_to_wcs_ptr(path, 0, &wpath);
file = os_wfopen(wpath, mode);
bfree(wpath);
}
return file;
#else
return path ? fopen(path, mode) : NULL;
#endif
}
int64_t os_fgetsize(FILE *file)
{
int64_t cur_offset = os_ftelli64(file);
int64_t size;
int errval = 0;
if (fseek(file, 0, SEEK_END) == -1)
return -1;
size = os_ftelli64(file);
if (size == -1)
errval = errno;
if (os_fseeki64(file, cur_offset, SEEK_SET) != 0 && errval != 0)
errno = errval;
return size;
}
int os_fseeki64(FILE *file, int64_t offset, int origin)
{
#ifdef _MSC_VER
return _fseeki64(file, offset, origin);
#else
return fseeko(file, offset, origin);
#endif
}
int64_t os_ftelli64(FILE *file)
{
#ifdef _MSC_VER
return _ftelli64(file);
#else
return ftello(file);
#endif
}
size_t os_fread_mbs(FILE *file, char **pstr)
{
size_t size = 0;
size_t len = 0;
fseek(file, 0, SEEK_END);
size = (size_t)os_ftelli64(file);
*pstr = NULL;
if (size > 0) {
char *mbstr = bmalloc(size+1);
fseek(file, 0, SEEK_SET);
size = fread(mbstr, 1, size, file);
if (size == 0) {
bfree(mbstr);
return 0;
}
mbstr[size] = 0;
len = os_mbs_to_utf8_ptr(mbstr, size, pstr);
bfree(mbstr);
}
return len;
}
size_t os_fread_utf8(FILE *file, char **pstr)
{
size_t size = 0;
size_t size_read;
size_t len = 0;
*pstr = NULL;
fseek(file, 0, SEEK_END);
size = (size_t)os_ftelli64(file);
if (size > 0) {
char bom[3];
char *utf8str;
off_t offset;
/* remove the ghastly BOM if present */
fseek(file, 0, SEEK_SET);
size_read = fread(bom, 1, 3, file);
if (size_read != 3)
return 0;
offset = (astrcmp_n(bom, "\xEF\xBB\xBF", 3) == 0) ? 3 : 0;
size -= offset;
if (size == 0)
return 0;
utf8str = bmalloc(size+1);
fseek(file, offset, SEEK_SET);
size = fread(utf8str, 1, size, file);
if (size == 0) {
bfree(utf8str);
return 0;
}
utf8str[size] = 0;
*pstr = utf8str;
}
return len;
}
char *os_quick_read_mbs_file(const char *path)
{
FILE *f = os_fopen(path, "rb");
char *file_string = NULL;
if (!f)
return NULL;
os_fread_mbs(f, &file_string);
fclose(f);
return file_string;
}
char *os_quick_read_utf8_file(const char *path)
{
FILE *f = os_fopen(path, "rb");
char *file_string = NULL;
if (!f)
return NULL;
os_fread_utf8(f, &file_string);
fclose(f);
return file_string;
}
bool os_quick_write_mbs_file(const char *path, const char *str, size_t len)
{
FILE *f = os_fopen(path, "wb");
char *mbs = NULL;
size_t mbs_len = 0;
if (!f)
return false;
mbs_len = os_utf8_to_mbs_ptr(str, len, &mbs);
if (mbs_len)
fwrite(mbs, 1, mbs_len, f);
bfree(mbs);
fclose(f);
return true;
}
bool os_quick_write_utf8_file(const char *path, const char *str, size_t len,
bool marker)
{
FILE *f = os_fopen(path, "wb");
if (!f)
return false;
if (marker)
fwrite("\xEF\xBB\xBF", 1, 3, f);
if (len)
fwrite(str, 1, len, f);
fclose(f);
return true;
}
bool os_quick_write_utf8_file_safe(const char *path, const char *str,
size_t len, bool marker, const char *temp_ext,
const char *backup_ext)
{
struct dstr backup_path = {0};
struct dstr temp_path = {0};
bool success = false;
if (!temp_ext || !*temp_ext) {
blog(LOG_ERROR, "os_quick_write_utf8_file_safe: invalid "
"temporary extension specified");
return false;
}
dstr_copy(&temp_path, path);
if (*temp_ext != '.')
dstr_cat(&temp_path, ".");
dstr_cat(&temp_path, temp_ext);
if (!os_quick_write_utf8_file(temp_path.array, str, len, marker)) {
goto cleanup;
}
if (backup_ext && *backup_ext) {
dstr_copy(&backup_path, path);
if (*backup_ext != '.')
dstr_cat(&backup_path, ".");
dstr_cat(&backup_path, backup_ext);
os_unlink(backup_path.array);
os_rename(path, backup_path.array);
dstr_free(&backup_path);
} else {
os_unlink(path);
}
os_rename(temp_path.array, path);
success = true;
cleanup:
dstr_free(&backup_path);
dstr_free(&temp_path);
return success;
}
int64_t os_get_file_size(const char *path)
{
FILE* f = os_fopen(path, "rb");
if (!f)
return -1;
int64_t sz = os_fgetsize(f);
fclose(f);
return sz;
}
size_t os_mbs_to_wcs(const char *str, size_t len, wchar_t *dst, size_t dst_size)
{
size_t out_len;
if (!str)
return 0;
out_len = dst ? (dst_size - 1) : mbstowcs(NULL, str, len);
if (dst) {
if (!dst_size)
return 0;
if (out_len)
out_len = mbstowcs(dst, str, out_len + 1);
dst[out_len] = 0;
}
return out_len;
}
size_t os_utf8_to_wcs(const char *str, size_t len, wchar_t *dst,
size_t dst_size)
{
size_t in_len;
size_t out_len;
if (!str)
return 0;
in_len = len ? len : strlen(str);
out_len = dst ? (dst_size - 1) : utf8_to_wchar(str, in_len, NULL, 0, 0);
if (dst) {
if (!dst_size)
return 0;
if (out_len)
out_len = utf8_to_wchar(str, in_len,
dst, out_len + 1, 0);
dst[out_len] = 0;
}
return out_len;
}
size_t os_wcs_to_mbs(const wchar_t *str, size_t len, char *dst, size_t dst_size)
{
size_t out_len;
if (!str)
return 0;
out_len = dst ? (dst_size - 1) : wcstombs(NULL, str, len);
if (dst) {
if (!dst_size)
return 0;
if (out_len)
out_len = wcstombs(dst, str, out_len + 1);
dst[out_len] = 0;
}
return out_len;
}
size_t os_wcs_to_utf8(const wchar_t *str, size_t len, char *dst,
size_t dst_size)
{
size_t in_len;
size_t out_len;
if (!str)
return 0;
in_len = (len != 0) ? len : wcslen(str);
out_len = dst ? (dst_size - 1) : wchar_to_utf8(str, in_len, NULL, 0, 0);
if (dst) {
if (!dst_size)
return 0;
if (out_len)
out_len = wchar_to_utf8(str, in_len,
dst, out_len + 1, 0);
dst[out_len] = 0;
}
return out_len;
}
size_t os_mbs_to_wcs_ptr(const char *str, size_t len, wchar_t **pstr)
{
if (str) {
size_t out_len = os_mbs_to_wcs(str, len, NULL, 0);
*pstr = bmalloc((out_len + 1) * sizeof(wchar_t));
return os_mbs_to_wcs(str, len, *pstr, out_len + 1);
} else {
*pstr = NULL;
return 0;
}
}
size_t os_utf8_to_wcs_ptr(const char *str, size_t len, wchar_t **pstr)
{
if (str) {
size_t out_len = os_utf8_to_wcs(str, len, NULL, 0);
*pstr = bmalloc((out_len + 1) * sizeof(wchar_t));
return os_utf8_to_wcs(str, len, *pstr, out_len + 1);
} else {
*pstr = NULL;
return 0;
}
}
size_t os_wcs_to_mbs_ptr(const wchar_t *str, size_t len, char **pstr)
{
if (str) {
size_t out_len = os_wcs_to_mbs(str, len, NULL, 0);
*pstr = bmalloc((out_len + 1) * sizeof(char));
return os_wcs_to_mbs(str, len, *pstr, out_len + 1);
} else {
*pstr = NULL;
return 0;
}
}
size_t os_wcs_to_utf8_ptr(const wchar_t *str, size_t len, char **pstr)
{
if (str) {
size_t out_len = os_wcs_to_utf8(str, len, NULL, 0);
*pstr = bmalloc((out_len + 1) * sizeof(char));
return os_wcs_to_utf8(str, len, *pstr, out_len + 1);
} else {
*pstr = NULL;
return 0;
}
}
size_t os_utf8_to_mbs_ptr(const char *str, size_t len, char **pstr)
{
char *dst = NULL;
size_t out_len = 0;
if (str) {
wchar_t *wstr = NULL;
size_t wlen = os_utf8_to_wcs_ptr(str, len, &wstr);
out_len = os_wcs_to_mbs_ptr(wstr, wlen, &dst);
bfree(wstr);
}
*pstr = dst;
return out_len;
}
size_t os_mbs_to_utf8_ptr(const char *str, size_t len, char **pstr)
{
char *dst = NULL;
size_t out_len = 0;
if (str) {
wchar_t *wstr = NULL;
size_t wlen = os_mbs_to_wcs_ptr(str, len, &wstr);
out_len = os_wcs_to_utf8_ptr(wstr, wlen, &dst);
bfree(wstr);
}
*pstr = dst;
return out_len;
}
/* locale independent double conversion from jansson, credit goes to them */
static inline void to_locale(char *str)
{
const char *point;
char *pos;
point = localeconv()->decimal_point;
if(*point == '.') {
/* No conversion needed */
return;
}
pos = strchr(str, '.');
if(pos)
*pos = *point;
}
static inline void from_locale(char *buffer)
{
const char *point;
char *pos;
point = localeconv()->decimal_point;
if(*point == '.') {
/* No conversion needed */
return;
}
pos = strchr(buffer, *point);
if(pos)
*pos = '.';
}
double os_strtod(const char *str)
{
char buf[64];
snprintf(buf, 64, "%s", str);
to_locale(buf);
return strtod(buf, NULL);
}
int os_dtostr(double value, char *dst, size_t size)
{
int ret;
char *start, *end;
size_t length;
ret = snprintf(dst, size, "%.17g", value);
if(ret < 0)
return -1;
length = (size_t)ret;
if(length >= size)
return -1;
from_locale(dst);
/* Make sure there's a dot or 'e' in the output. Otherwise
a real is converted to an integer when decoding */
if(strchr(dst, '.') == NULL && strchr(dst, 'e') == NULL) {
if(length + 3 >= size) {
/* No space to append ".0" */
return -1;
}
dst[length] = '.';
dst[length + 1] = '0';
dst[length + 2] = '\0';
length += 2;
}
/* Remove leading '+' from positive exponent. Also remove leading
zeros from exponents (added by some printf() implementations) */
start = strchr(dst, 'e');
if(start) {
start++;
end = start + 1;
if(*start == '-')
start++;
while(*end == '0')
end++;
if(end != start) {
memmove(start, end, length - (size_t)(end - dst));
length -= (size_t)(end - start);
}
}
return (int)length;
}
static int recursive_mkdir(char *path)
{
char *last_slash;
int ret;
ret = os_mkdir(path);
if (ret != MKDIR_ERROR)
return ret;
last_slash = strrchr(path, '/');
if (!last_slash)
return MKDIR_ERROR;
*last_slash = 0;
ret = recursive_mkdir(path);
*last_slash = '/';
if (ret == MKDIR_ERROR)
return MKDIR_ERROR;
ret = os_mkdir(path);
return ret;
}
int os_mkdirs(const char *dir)
{
struct dstr dir_str;
int ret;
dstr_init_copy(&dir_str, dir);
dstr_replace(&dir_str, "\\", "/");
ret = recursive_mkdir(dir_str.array);
dstr_free(&dir_str);
return ret;
}