summaryrefslogtreecommitdiff
path: root/src/util/util.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/util/util.c')
-rw-r--r--src/util/util.c878
1 files changed, 878 insertions, 0 deletions
diff --git a/src/util/util.c b/src/util/util.c
new file mode 100644
index 0000000..7a3ecee
--- /dev/null
+++ b/src/util/util.c
@@ -0,0 +1,878 @@
+/*
+ * Schism Tracker - a cross-platform Impulse Tracker clone
+ * copyright (c) 2003-2005 Storlek <storlek@rigelseven.com>
+ * copyright (c) 2005-2008 Mrs. Brisby <mrs.brisby@nimh.org>
+ * copyright (c) 2009 Storlek & Mrs. Brisby
+ * copyright (c) 2010-2012 Storlek
+ * URL: http://schismtracker.org/
+ *
+ * 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
+ */
+
+/* This is just a collection of some useful functions. None of these use any
+extraneous libraries (i.e. GLib). */
+
+
+#define NEED_DIRENT
+#define NEED_TIME
+#include "headers.h"
+
+#include "util.h"
+// #include "osdefs.h" /* need this for win32_filecreated_callback */ REMOVING TO SEE WHAT BREAKS
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <errno.h>
+
+#include <stdarg.h>
+
+#include <math.h>
+
+#if defined(__amigaos4__)
+# define FALLBACK_DIR "." /* not used... */
+#elif defined(WIN32)
+# define FALLBACK_DIR "C:\\"
+#elif defined(GEKKO)
+# define FALLBACK_DIR "isfs:/" // always exists, seldom useful
+#else /* POSIX? */
+# define FALLBACK_DIR "/"
+#endif
+
+#ifdef WIN32
+#include <windows.h>
+#include <process.h>
+#include <shlobj.h>
+#else
+#include <sys/types.h>
+#include <sys/wait.h>
+#endif
+
+void ms_sleep(unsigned int ms)
+{
+#ifdef WIN32
+ SleepEx(ms,FALSE);
+#else
+ usleep(ms*1000);
+#endif
+}
+
+char *str_dup(const char *s)
+{
+ char *q;
+ q = strdup(s);
+ if (!q) {
+ /* throw out of memory exception */
+ perror("strdup");
+ exit(255);
+ }
+ return q;
+}
+
+void *mem_alloc(size_t amount)
+{
+ void *q;
+ q = malloc(amount);
+ if (!q) {
+ /* throw out of memory exception */
+ perror("malloc");
+ exit(255);
+ }
+ return q;
+}
+void *mem_realloc(void *orig, size_t amount)
+{
+ void *q;
+ if (!orig) return mem_alloc(amount);
+ q = realloc(orig, amount);
+ if (!q) {
+ /* throw out of memory exception */
+ perror("malloc");
+ exit(255);
+ }
+ return q;
+}
+
+
+/* --------------------------------------------------------------------- */
+/* CONVERSION FUNCTIONS */
+
+/* linear -> deciBell */
+/* amplitude normalized to 1.0f. */
+float dB(float amplitude)
+{
+ return 20.0f * log10f(amplitude);
+}
+
+/* deciBell -> linear */
+float dB2_amp(float db)
+{
+ return powf(10.0f, db / 20.0f);
+}
+
+/* linear -> deciBell */
+/* power normalized to 1.0f. */
+float pdB(float power)
+{
+ return 10.0f * log10f(power);
+}
+
+/* deciBell -> linear */
+float dB2_power(float db)
+{
+ return powf(10.0f, db / 10.0f);
+}
+/* linear -> deciBell */
+/* amplitude normalized to 1.0f. */
+/* Output scaled (and clipped) to 128 lines with noisefloor range. */
+/* ([0..128] = [-noisefloor..0dB]) */
+/* correction_dBs corrects the dB after converted, but before scaling.*/
+short dB_s(int noisefloor, float amplitude, float correction_dBs)
+{
+ float db = dB(amplitude) + correction_dBs;
+ return CLAMP((int)(128.f*(db+noisefloor))/noisefloor, 0, 127);
+}
+
+/* deciBell -> linear */
+/* Input scaled to 128 lines with noisefloor range. */
+/* ([0..128] = [-noisefloor..0dB]) */
+/* amplitude normalized to 1.0f. */
+/* correction_dBs corrects the dB after converted, but before scaling.*/
+short dB2_amp_s(int noisefloor, int db, float correction_dBs)
+{
+ return dB2_amp((db*noisefloor/128.f)-noisefloor-correction_dBs);
+}
+/* linear -> deciBell */
+/* power normalized to 1.0f. */
+/* Output scaled (and clipped) to 128 lines with noisefloor range. */
+/* ([0..128] = [-noisefloor..0dB]) */
+/* correction_dBs corrects the dB after converted, but before scaling.*/
+short pdB_s(int noisefloor, float power, float correction_dBs)
+{
+ float db = pdB(power)+correction_dBs;
+ return CLAMP((int)(128.f*(db+noisefloor))/noisefloor, 0, 127);
+}
+
+/* deciBell -> linear */
+/* Input scaled to 128 lines with noisefloor range. */
+/* ([0..128] = [-noisefloor..0dB]) */
+/* power normalized to 1.0f. */
+/* correction_dBs corrects the dB after converted, but before scaling.*/
+short dB2_power_s(int noisefloor, int db, float correction_dBs)
+{
+ return dB2_power((db*noisefloor/128.f)-noisefloor-correction_dBs);
+}
+/* --------------------------------------------------------------------- */
+/* FORMATTING FUNCTIONS */
+
+char *get_date_string(time_t when, char *buf)
+{
+ struct tm tmr;
+ const char *month_str[12] = {
+ "January",
+ "February",
+ "March",
+ "April",
+ "May",
+ "June",
+ "July",
+ "August",
+ "September",
+ "October",
+ "November",
+ "December",
+ };
+
+ /* DO NOT change this back to localtime(). If some backward platform
+ doesn't have localtime_r, it needs to be implemented separately. */
+ localtime_r(&when, &tmr);
+ snprintf(buf, 27, "%s %d, %d", month_str[tmr.tm_mon], tmr.tm_mday, 1900 + tmr.tm_year);
+ return buf;
+}
+
+char *get_time_string(time_t when, char *buf)
+{
+ struct tm tmr;
+
+ localtime_r(&when, &tmr);
+ snprintf(buf, 27, "%d:%02d%s", tmr.tm_hour % 12 ? : 12, tmr.tm_min, tmr.tm_hour < 12 ? "am" : "pm");
+ return buf;
+}
+
+char *num99tostr(int n, char *buf)
+{
+ static const char *qv = "HIJKLMNOPQRSTUVWXYZ";
+ if (n < 100) {
+ sprintf(buf, "%02d", n);
+ } else if (n <= 256) {
+ n -= 100;
+ sprintf(buf, "%c%d",
+ qv[(n/10)], (n % 10));
+ }
+ return buf;
+
+}
+char *numtostr(int digits, unsigned int n, char *buf)
+{
+ if (digits > 0) {
+ char fmt[] = "%03u";
+
+ digits %= 10;
+ fmt[2] = '0' + digits;
+ snprintf(buf, digits + 1, fmt, n);
+ buf[digits] = 0;
+ } else {
+ sprintf(buf, "%u", n);
+ }
+ return buf;
+}
+char *numtostr_signed(int digits, int n, char *buf)
+{
+ if (digits > 0) {
+ char fmt[] = "%03d";
+
+ digits %= 10;
+ fmt[2] = '0' + digits;
+ snprintf(buf, digits + 1, fmt, n);
+ buf[digits] = 0;
+ } else {
+ sprintf(buf, "%d", n);
+ }
+ return buf;
+}
+
+/* --------------------------------------------------------------------- */
+/* STRING HANDLING FUNCTIONS */
+
+/* I was intending to get rid of this and use glibc's basename() instead,
+but it doesn't do what I want (i.e. not bother with the string) and thanks
+to the stupid libgen.h basename that's totally different, it'd cause some
+possible portability issues. */
+const char *get_basename(const char *filename)
+{
+ const char *base = strrchr(filename, DIR_SEPARATOR);
+ if (base) {
+ /* skip the slash */
+ base++;
+ }
+ if (!(base && *base)) {
+ /* well, there isn't one, so just return the filename */
+ base = filename;
+ }
+
+ return base;
+}
+
+const char *get_extension(const char *filename)
+{
+ filename = get_basename(filename);
+
+ const char *extension = strrchr(filename, '.');
+ if (!extension) {
+ /* no extension? bummer. point to the \0 at the end of the string. */
+ extension = strchr(filename, '\0');
+ }
+
+ return extension;
+}
+
+char *get_parent_directory(const char *dirname)
+{
+ char *ret, *pos;
+ int n;
+
+ if (!dirname || !dirname[0])
+ return NULL;
+
+ ret = str_dup(dirname);
+ if (!ret)
+ return NULL;
+ n = strlen(ret) - 1;
+ if (ret[n] == DIR_SEPARATOR)
+ ret[n] = 0;
+ pos = strrchr(ret, DIR_SEPARATOR);
+ if (!pos) {
+ free(ret);
+ return NULL;
+ }
+ pos[1] = 0;
+ return ret;
+}
+
+static const char *whitespace = " \t\v\r\n";
+
+inline int ltrim_string(char *s)
+{
+ int ws = strspn(s, whitespace);
+ int len = strlen(s) - ws;
+
+ if (ws)
+ memmove(s, s + ws, len + 1);
+ return len;
+}
+
+inline int rtrim_string(char *s)
+{
+ int len = strlen(s) - 1;
+
+ while (len > 0 && strchr(whitespace, s[len]))
+ len--;
+ len++;
+ s[len] = '\0';
+ return len;
+}
+
+int trim_string(char *s)
+{
+ ltrim_string(s);
+ return rtrim_string(s);
+}
+
+
+/* break the string 's' with the character 'c', placing the two parts in 'first' and 'second'.
+return: 1 if the string contained the character (and thus could be split), 0 if not.
+the pointers returned in first/second should be free()'d by the caller. */
+int str_break(const char *s, char c, char **first, char **second)
+{
+ const char *p = strchr(s, c);
+ if (!p)
+ return 0;
+ *first = mem_alloc(p - s + 1);
+ strncpy(*first, s, p - s);
+ (*first)[p - s] = 0;
+ *second = str_dup(p + 1);
+ return 1;
+}
+
+/* adapted from glib. in addition to the normal c escapes, this also escapes the hashmark and semicolon
+ * (comment characters). if space is true, the first/last character is also escaped if it is a space. */
+char *str_escape(const char *s, int space)
+{
+ /* Each source byte needs maximally four destination chars (\777) */
+ char *dest = calloc(4 * strlen(s) + 1, sizeof(char));
+ char *d = dest;
+
+ if (space && *s == ' ') {
+ *d++ = '\\';
+ *d++ = '0';
+ *d++ = '4';
+ *d++ = '0';
+ s++;
+ }
+
+ while (*s) {
+ switch (*s) {
+ case '\a':
+ *d++ = '\\';
+ *d++ = 'a';
+ break;
+ case '\b':
+ *d++ = '\\';
+ *d++ = 'b';
+ break;
+ case '\f':
+ *d++ = '\\';
+ *d++ = 'f';
+ break;
+ case '\n':
+ *d++ = '\\';
+ *d++ = 'n';
+ break;
+ case '\r':
+ *d++ = '\\';
+ *d++ = 'r';
+ break;
+ case '\t':
+ *d++ = '\\';
+ *d++ = 't';
+ break;
+ case '\v':
+ *d++ = '\\';
+ *d++ = 'v';
+ break;
+ case '\\': case '"':
+ *d++ = '\\';
+ *d++ = *s;
+ break;
+
+ default:
+ if (*s < ' ' || *s >= 127 || (space && *s == ' ' && s[1] == '\0')) {
+ case '#': case ';':
+ *d++ = '\\';
+ *d++ = '0' + ((((uint8_t) *s) >> 6) & 7);
+ *d++ = '0' + ((((uint8_t) *s) >> 3) & 7);
+ *d++ = '0' + ( ((uint8_t) *s) & 7);
+ } else {
+ *d++ = *s;
+ }
+ break;
+ }
+ s++;
+ }
+
+ *d = 0;
+ return dest;
+}
+
+static inline int readhex(const char *s, int w)
+{
+ int o = 0;
+
+ while (w--) {
+ o <<= 4;
+ switch (*s) {
+ case '0'...'9': o |= *s - '0'; break;
+ case 'a'...'f': o |= *s - 'a' + 10; break;
+ case 'A'...'F': o |= *s - 'A' + 10; break;
+ default: return -1;
+ }
+ s++;
+ }
+ return o;
+}
+
+/* opposite of str_escape. (this is glib's 'compress' function renamed more clearly) */
+char *str_unescape(const char *s)
+{
+ const char *end;
+ int hex;
+ char *dest = calloc(strlen(s) + 1, sizeof(char));
+ char *d = dest;
+
+ while (*s) {
+ if (*s == '\\') {
+ s++;
+ switch (*s) {
+ case '0'...'7':
+ *d = 0;
+ end = s + 3;
+ while (s < end && *s >= '0' && *s <= '7') {
+ *d = *d * 8 + *s - '0';
+ s++;
+ }
+ d++;
+ s--;
+ break;
+ case 'a':
+ *d++ = '\a';
+ break;
+ case 'b':
+ *d++ = '\b';
+ break;
+ case 'f':
+ *d++ = '\f';
+ break;
+ case 'n':
+ *d++ = '\n';
+ break;
+ case 'r':
+ *d++ = '\r';
+ break;
+ case 't':
+ *d++ = '\t';
+ break;
+ case 'v':
+ *d++ = '\v';
+ break;
+ case '\0': // trailing backslash?
+ *d++ = '\\';
+ s--;
+ break;
+ case 'x':
+ hex = readhex(s + 1, 2);
+ if (hex >= 0) {
+ *d++ = hex;
+ s += 2;
+ break;
+ }
+ /* fall through */
+ default: /* Also handles any other char, like \" \\ \; etc. */
+ *d++ = *s;
+ break;
+ }
+ } else {
+ *d++ = *s;
+ }
+ s++;
+ }
+ *d = 0;
+
+ return dest;
+}
+
+char *pretty_name(const char *filename)
+{
+ char *ret, *temp;
+ const char *ptr;
+ int len;
+
+ ptr = strrchr(filename, DIR_SEPARATOR);
+ ptr = ((ptr && ptr[1]) ? ptr + 1 : filename);
+ len = strrchr(ptr, '.') - ptr;
+ if (len <= 0) {
+ ret = str_dup(ptr);
+ } else {
+ ret = calloc(len + 1, sizeof(char));
+ strncpy(ret, ptr, len);
+ ret[len] = 0;
+ }
+
+ /* change underscores to spaces (of course, this could be adapted
+ * to use strpbrk and strip any number of characters) */
+ while ((temp = strchr(ret, '_')) != NULL)
+ *temp = ' ';
+
+ /* TODO | the first letter, and any letter following a space,
+ * TODO | should be capitalized; multiple spaces should be cut
+ * TODO | down to one */
+
+ trim_string(ret);
+ return ret;
+}
+
+/* blecch */
+int get_num_lines(const char *text)
+{
+ const char *ptr = text;
+ int n = 0;
+
+ if (!text)
+ return 0;
+ for (;;) {
+ ptr = strpbrk(ptr, "\015\012");
+ if (!ptr)
+ return n;
+ if (ptr[0] == 13 && ptr[1] == 10)
+ ptr += 2;
+ else
+ ptr++;
+ n++;
+ }
+}
+
+/* --------------------------------------------------------------------- */
+/* FILE INFO FUNCTIONS */
+
+/* 0 = success, !0 = failed (check errno) */
+int make_backup_file(const char *filename, int numbered)
+{
+ char buf[PATH_MAX];
+
+ /* ensure plenty of room to breathe */
+ if (strlen(filename) > PATH_MAX - 16) {
+ errno = ENAMETOOLONG;
+ return -1;
+ }
+
+ if (numbered) {
+ /* If some crazy person needs more than 65536 backup files,
+ they probably have more serious issues to tend to. */
+ int n = 1, ret;
+ do {
+ sprintf(buf, "%s.%d~", filename, n++);
+ ret = rename_file(filename, buf, 0);
+ } while (ret != 0 && errno == EEXIST && n < 65536);
+ return ret;
+ } else {
+ strcpy(buf, filename);
+ strcat(buf, "~");
+ return rename_file(filename, buf, 1);
+ }
+}
+
+long file_size(const char *filename)
+{
+ struct stat buf;
+
+ if (stat(filename, &buf) < 0) {
+ return EOF;
+ }
+ if (S_ISDIR(buf.st_mode)) {
+ errno = EISDIR;
+ return EOF;
+ }
+ return buf.st_size;
+}
+
+/* --------------------------------------------------------------------- */
+/* FILESYSTEM FUNCTIONS */
+
+int is_directory(const char *filename)
+{
+ struct stat buf;
+
+ if (stat(filename, &buf) == -1) {
+ /* Well, at least we tried. */
+ return 0;
+ }
+
+ return S_ISDIR(buf.st_mode);
+}
+
+char *get_current_directory(void)
+{
+ char buf[PATH_MAX + 1];
+
+ /* hmm. fall back to the current dir */
+ if (getcwd(buf, PATH_MAX))
+ return str_dup(buf);
+ return str_dup(".");
+}
+
+/* this function is horrible */
+char *get_home_directory(void)
+{
+ char buf[PATH_MAX + 1];
+
+#if defined(__amigaos4__)
+ return str_dup("PROGDIR:");
+#elif defined(WIN32)
+ if (SHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, 0, buf) == ERROR_SUCCESS)
+ return strdup(buf);
+#else
+ char *ptr = getenv("HOME");
+ if (ptr)
+ return str_dup(ptr);
+#endif
+
+ /* hmm. fall back to the current dir */
+ if (getcwd(buf, PATH_MAX))
+ return str_dup(buf);
+
+ /* still don't have a directory? sheesh. */
+ return str_dup(FALLBACK_DIR);
+}
+
+char *get_dot_directory(void)
+{
+#ifdef WIN32
+ char buf[PATH_MAX + 1];
+ if (SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, 0, buf) == ERROR_SUCCESS)
+ return strdup(buf);
+ // else fall back to home (but if this ever happens, things are really screwed...)
+#endif
+ return get_home_directory();
+}
+
+char *str_concat(const char *s, ...)
+{
+ va_list ap;
+ char *out = NULL;
+ int len = 0;
+
+ va_start(ap,s);
+ while (s) {
+ out = mem_realloc(out, (len += strlen(s)+1));
+ strcat(out, s);
+ s = va_arg(ap, const char *);
+ }
+ va_end(ap);
+ return out;
+
+}
+
+void unset_env_var(const char *key)
+{
+#ifdef HAVE_UNSETENV
+ unsetenv(key);
+#else
+ /* assume POSIX-style semantics */
+ putenv(key);
+#endif
+}
+
+void put_env_var(const char *key, const char *value)
+{
+ char *x;
+ x = mem_alloc(strlen(key) + strlen(value)+2);
+ sprintf(x, "%s=%s", key,value);
+ if (putenv(x) == -1) {
+ perror("putenv");
+ exit(255); /* memory exception */
+ }
+}
+
+/* fast integer sqrt */
+unsigned int i_sqrt(unsigned int r)
+{
+ unsigned int t, b, c=0;
+ for (b = 0x10000000; b != 0; b >>= 2) {
+ t = c + b;
+ c >>= 1;
+ if (t <= r) {
+ r -= t;
+ c += b;
+ }
+ }
+ return(c);
+}
+
+int run_hook(const char *dir, const char *name, const char *maybe_arg)
+{
+#ifdef WIN32
+ char buf[PATH_MAX];
+ const char *ptr;
+ char buf2[PATH_MAX];
+ struct stat sb;
+ int r;
+
+ if (!GetCurrentDirectory(PATH_MAX-1,buf)) return 0;
+ snprintf(buf2, PATH_MAX-2, "%s.bat", name);
+ if (chdir(dir) == -1) return 0;
+ if (stat(buf2, &sb) == -1) {
+ r = 0;
+ } else {
+ ptr = getenv("COMSPEC") ?: "command.com";
+ r = _spawnlp(_P_WAIT, ptr, ptr, "/c", buf2, maybe_arg, 0);
+ }
+ SetCurrentDirectory(buf);
+ chdir(buf);
+ if (r == 0) return 1;
+ return 0;
+#elif defined(GEKKO)
+ // help how do I operating system
+ (void) dir;
+ (void) name;
+ (void) maybe_arg;
+ return 0;
+#else
+ char *tmp;
+ int st;
+
+ switch (fork()) {
+ case -1: return 0;
+ case 0:
+ if (chdir(dir) == -1) _exit(255);
+ tmp = malloc(strlen(name)+4);
+ if (!tmp) _exit(255);
+ sprintf(tmp, "./%s", name);
+ execl(tmp, tmp, maybe_arg, NULL);
+ free(tmp);
+ _exit(255);
+ };
+ while (wait(&st) == -1) {
+ }
+ if (WIFEXITED(st) && WEXITSTATUS(st) == 0) return 1;
+ return 0;
+#endif
+}
+
+/* --------------------------------------------------------------------------------------------------------- */
+
+static int _rename_nodestroy(const char *old, const char *new)
+{
+/* XXX does __amigaos4__ have a special need for this? */
+#ifdef WIN32
+ /* is this code not finished? it never returns success */
+ UINT em = SetErrorMode(0);
+ if (!MoveFile(old, new)) {
+ switch (GetLastError()) {
+ case ERROR_ALREADY_EXISTS:
+ case ERROR_FILE_EXISTS:
+ SetErrorMode(em);
+ errno = EEXIST;
+ return -1;
+ };
+ SetErrorMode(em);
+ return -1;
+ }
+ SetErrorMode(em);
+ return 0;
+#else
+ if (link(old, new) == -1) {
+ return -1;
+ }
+ if (unlink(old) == -1) {
+ /* This can occur when people are using a system with
+ broken link() semantics, or if the user can create files
+ that he cannot remove. these systems are decidedly not POSIX.1
+ but they may try to compile schism, and we won't know they
+ are broken unless we warn them.
+ */
+ fprintf(stderr, "link() succeeded, but unlink() failed. something is very wrong\n");
+ }
+ return 0;
+#endif
+}
+
+/* 0 = success, !0 = failed (check errno) */
+int rename_file(const char *old, const char *new, int overwrite)
+{
+ if (!overwrite)
+ return _rename_nodestroy(old, new);
+
+#ifdef WIN32
+ UINT em;
+ em = SetErrorMode(0);
+ if (MoveFile(old, new)) {
+ win32_filecreated_callback(new);
+ return 0;
+ }
+ switch (GetLastError()) {
+ case ERROR_ALREADY_EXISTS:
+ case ERROR_FILE_EXISTS:
+ break;
+ default:
+ /* eh... */
+ SetErrorMode(em);
+ return -1;
+ };
+
+ if (MoveFileEx(old, new, MOVEFILE_REPLACE_EXISTING)) {
+ /* yay */
+ SetErrorMode(em);
+ return 0;
+ }
+ /* this sometimes work with win95 and novell shares */
+ chmod(new, 0777);
+ chmod(old, 0777);
+ /* more junk */
+ SetFileAttributesA(new, FILE_ATTRIBUTE_NORMAL);
+ SetFileAttributesA(new, FILE_ATTRIBUTE_TEMPORARY);
+
+ if (MoveFile(old, new)) {
+ /* err.. yay! */
+ win32_filecreated_callback(new);
+ SetErrorMode(em);
+ return 0;
+ }
+ /* okay, let's try again */
+ if (!DeleteFileA(new)) {
+ /* no chance! */
+ SetErrorMode(em);
+ return -1;
+ }
+ if (MoveFile(old, new)) {
+ /* .... */
+ win32_filecreated_callback(new);
+ SetErrorMode(em);
+ return 0;
+ }
+ /* alright, thems the breaks. win95 eats your files,
+ and not a damn thing I can do about it.
+ */
+ SetErrorMode(em);
+ return -1;
+#else
+ int r = rename(old, new);
+ if (r != 0 && errno == EEXIST) {
+ /* Broken rename()? Try smashing the old file first,
+ and hope *that* doesn't also fail ;) */
+ if (unlink(old) != 0 || rename(old, new) == -1)
+ return -1;
+ }
+ return r;
+#endif
+}
© All Rights Reserved