/* This file is part of Libspectre.
*
* Copyright (C) 2007 Albert Astals Cid <aacid@kde.org>
* Copyright (C) 2007 Carlos Garcia Campos <carlosgc@gnome.org>
*
* Libspectre 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, or (at your option)
* any later version.
*
* Libspectre 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <locale.h>
#include "spectre-utils.h"
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef WIN32
#include <windows.h>
#endif
static unsigned long
_spectre_get_pid (void)
{
#if defined(HAVE_SYS_TYPES_H) && defined(HAVE_UNISTD_H)
return getpid ();
#elif defined(WIN32)
return GetCurrentProcessId ();
#endif
}
static int warn_initted = FALSE;
static int fatal_warnings = FALSE;
static int fatal_warnings_on_check_failed = FALSE;
static void
init_warnings (void)
{
const char *s;
if (warn_initted)
return;
warn_initted = TRUE;
s = getenv ("SPECTRE_FATAL_WARNINGS");
if (!s || !(*s))
return;
if (*s == '0') {
fatal_warnings = FALSE;
fatal_warnings_on_check_failed = FALSE;
} else if (*s == '1') {
fatal_warnings = TRUE;
fatal_warnings_on_check_failed = TRUE;
} else {
fprintf (stderr,
"SPECTRE_FATAL_WARNINGS should be set to 0 or 1 if set, not '%s'",
s);
}
}
/**
* Prints a warning message to stderr. Can optionally be made to exit
* fatally by setting SPECTRE_FATAL_WARNINGS, but this is rarely
* used. This function should be considered pretty much equivalent to
* fprintf(stderr). _spectre_warn_check_failed() on the other hand is
* suitable for use when a programming mistake has been made.
*/
void
_spectre_warn (const char *format,
...)
{
va_list args;
if (!warn_initted)
init_warnings ();
va_start (args, format);
vfprintf (stderr, format, args);
va_end (args);
if (fatal_warnings) {
fflush (stderr);
abort ();
}
}
/**
* Prints a "critical" warning to stderr when an assertion fails;
* differs from _spectre_warn primarily in that it prefixes the pid and
* defaults to fatal. This should be used only when a programming
* error has been detected. (NOT for unavoidable errors that an app
* might handle. Calling this means "there is a bug"
*/
void
_spectre_warn_check_failed (const char *format,
...)
{
va_list args;
if (!warn_initted)
init_warnings ();
fprintf (stderr, "process %lu: ", _spectre_get_pid ());
va_start (args, format);
vfprintf (stderr, format, args);
va_end (args);
if (fatal_warnings_on_check_failed) {
fflush (stderr);
abort ();
}
}
#ifndef SPECTRE_DISABLE_ASSERT
void
_spectre_real_assert (int condition,
const char *condition_text,
const char *file,
int line,
const char *func)
{
if (_SPECTRE_UNLIKELY (!condition)) {
_spectre_warn ("%lu: assertion failed \"%s\" file \"%s\" line %d function %s\n",
_spectre_get_pid (), condition_text, file, line, func);
abort ();
}
}
#endif /* SPECTRE_DISABLE_ASSERT */
static char *
spectre_strdup_vprintf (const char *format,
va_list args)
{
char *string = NULL;
int len;
#if defined(HAVE_VASPRINTF)
len = vasprintf (&string, format, args);
if (len < 0)
string = NULL;
#else /* !HAVE_VASPRINTF */
va_list args_copy;
int n;
char c;
SPECTRE_VA_COPY (args_copy, args);
#if HAVE__VSCPRINTF
n = _vscprintf (format, args);
#else
n = vsnprintf (&c, 1, format, args);
#endif
string = malloc ((n + 1) * sizeof (char));
if (string) {
len = vsprintf (string, format, args_copy);
if (len < 0) {
free (string);
string = NULL;
}
}
va_end (args_copy);
#endif
return string;
}
char *
_spectre_strdup_printf (const char *format, ...)
{
char *buffer;
va_list args;
va_start (args, format);
buffer = spectre_strdup_vprintf (format, args);
va_end (args);
return buffer;
}
char *
_spectre_strdup (const char *str)
{
size_t len;
char *copy;
if (!str)
return NULL;
len = strlen (str) + 1;
copy = malloc (len);
if (!copy)
return NULL;
memcpy (copy, str, len);
return copy;
}
#define TOLOWER(c) (((c) >= 'A' && (c) <= 'Z') ? (c) - 'A' + 'a' : (c))
int
_spectre_strncasecmp (const char *s1,
const char *s2,
size_t n)
{
int c1, c2;
while (n && *s1 && *s2) {
n -= 1;
c1 = (int)(unsigned char) TOLOWER (*s1);
c2 = (int)(unsigned char) TOLOWER (*s2);
if (c1 != c2)
return (c1 - c2);
s1++;
s2++;
}
return (n) ? (((int) (unsigned char) *s1) - ((int) (unsigned char) *s2)) : 0;
}
int
_spectre_strcasecmp (const char *s1,
const char *s2)
{
int c1, c2;
while (*s1 && *s2) {
c1 = (int)(unsigned char) TOLOWER (*s1);
c2 = (int)(unsigned char) TOLOWER (*s2);
if (c1 != c2)
return (c1 - c2);
s1++;
s2++;
}
return (((int)(unsigned char) *s1) - ((int)(unsigned char) *s2));
}
#define ascii_isspace(c) \
(c == ' ' || c == '\f' || c == '\n' || c == '\r' || c == '\t' || c == '\v')
#define ascii_isdigit(c) \
(c >= '0' && c <= '9')
/* This function behaves like the standard strtod() function
* does in the C locale. It does this without actually changing
* the current locale, since that would not be thread-safe.
* A limitation of the implementation is that this function
* will still accept localized versions of infinities and NANs.
*/
double
_spectre_strtod (const char *nptr,
char **endptr)
{
char *fail_pos;
double val;
struct lconv *locale_data;
const char *decimal_point;
int decimal_point_len;
const char *p, *decimal_point_pos;
const char *end = NULL; /* Silence gcc */
int strtod_errno;
fail_pos = NULL;
locale_data = localeconv ();
decimal_point = locale_data->decimal_point;
decimal_point_len = strlen (decimal_point);
decimal_point_pos = NULL;
end = NULL;
if (decimal_point[0] != '.' || decimal_point[1] != 0) {
p = nptr;
/* Skip leading space */
while (ascii_isspace (*p))
p++;
/* Skip leading optional sign */
if (*p == '+' || *p == '-')
p++;
if (ascii_isdigit (*p) || *p == '.') {
while (ascii_isdigit (*p))
p++;
if (*p == '.')
decimal_point_pos = p++;
while (ascii_isdigit (*p))
p++;
if (*p == 'e' || *p == 'E')
p++;
if (*p == '+' || *p == '-')
p++;
while (ascii_isdigit (*p))
p++;
end = p;
}
/* For the other cases, we need not convert the decimal point */
}
if (decimal_point_pos) {
char *copy, *c;
/* We need to convert the '.' to the locale specific decimal point */
copy = (char *) malloc (end - nptr + 1 + decimal_point_len);
c = copy;
memcpy (c, nptr, decimal_point_pos - nptr);
c += decimal_point_pos - nptr;
memcpy (c, decimal_point, decimal_point_len);
c += decimal_point_len;
memcpy (c, decimal_point_pos + 1, end - (decimal_point_pos + 1));
c += end - (decimal_point_pos + 1);
*c = 0;
errno = 0;
val = strtod (copy, &fail_pos);
strtod_errno = errno;
if (fail_pos) {
if (fail_pos - copy > decimal_point_pos - nptr)
fail_pos = (char *)nptr + (fail_pos - copy) - (decimal_point_len - 1);
else
fail_pos = (char *)nptr + (fail_pos - copy);
}
free (copy);
} else if (end) {
char *copy;
copy = (char *) malloc (end - (char *)nptr + 1);
memcpy (copy, nptr, end - nptr);
*(copy + (end - (char *)nptr)) = 0;
errno = 0;
val = strtod (copy, &fail_pos);
strtod_errno = errno;
if (fail_pos) {
fail_pos = (char *)nptr + (fail_pos - copy);
}
free (copy);
} else {
errno = 0;
val = strtod (nptr, &fail_pos);
strtod_errno = errno;
}
if (endptr)
*endptr = fail_pos;
errno = strtod_errno;
return val;
}