/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) version 3.
*
* 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with the program; if not, see <http://www.gnu.org/licenses/>
*
*
* Authors:
* Srinivasa Ragavan <sragavan@gnome.org>
*
* Copyright (C) 2012 Intel Corporation (www.intel.com)
*
*/
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <ctype.h>
#include <math.h>
#include <string.h>
#include <locale.h>
#include "evolution-util.h"
/**
* e_flexible_strtod:
* @nptr: the string to convert to a numeric value.
* @endptr: if non-NULL, it returns the character after
* the last character used in the conversion.
*
* Converts a string to a gdouble value. This function detects
* strings either in the standard C locale or in the current locale.
*
* This function is typically used when reading configuration files or
* other non-user input that should not be locale dependent, but may
* have been in the past. To handle input from the user you should
* normally use the locale-sensitive system strtod function.
*
* To convert from a double to a string in a locale-insensitive way, use
* @g_ascii_dtostr.
*
* Returns: the gdouble value
**/
gdouble
e_flexible_strtod (const gchar *nptr,
gchar **endptr)
{
gchar *fail_pos;
gdouble val;
struct lconv *locale_data;
const gchar *decimal_point;
gint decimal_point_len;
const gchar *p, *decimal_point_pos;
const gchar *end = NULL; /* Silence gcc */
gchar *copy, *c;
g_return_val_if_fail (nptr != NULL, 0);
fail_pos = NULL;
locale_data = localeconv ();
decimal_point = locale_data->decimal_point;
decimal_point_len = strlen (decimal_point);
g_return_val_if_fail (decimal_point_len != 0, 0);
decimal_point_pos = NULL;
if (!strcmp (decimal_point, "."))
return strtod (nptr, endptr);
p = nptr;
/* Skip leading space */
while (isspace ((guchar) * p))
p++;
/* Skip leading optional sign */
if (*p == '+' || *p == '-')
p++;
if (p[0] == '0' &&
(p[1] == 'x' || p[1] == 'X')) {
p += 2;
/* HEX - find the (optional) decimal point */
while (isxdigit ((guchar) * p))
p++;
if (*p == '.') {
decimal_point_pos = p++;
while (isxdigit ((guchar) * p))
p++;
if (*p == 'p' || *p == 'P')
p++;
if (*p == '+' || *p == '-')
p++;
while (isdigit ((guchar) * p))
p++;
end = p;
} else if (strncmp (p, decimal_point, decimal_point_len) == 0) {
return strtod (nptr, endptr);
}
} else {
while (isdigit ((guchar) * p))
p++;
if (*p == '.') {
decimal_point_pos = p++;
while (isdigit ((guchar) * p))
p++;
if (*p == 'e' || *p == 'E')
p++;
if (*p == '+' || *p == '-')
p++;
while (isdigit ((guchar) * p))
p++;
end = p;
} else if (strncmp (p, decimal_point, decimal_point_len) == 0) {
return strtod (nptr, endptr);
}
}
/* For the other cases, we need not convert the decimal point */
if (!decimal_point_pos)
return strtod (nptr, endptr);
/* We need to convert the '.' to the locale specific decimal point */
copy = g_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;
val = strtod (copy, &fail_pos);
if (fail_pos) {
if (fail_pos > decimal_point_pos)
fail_pos =
(gchar *) nptr + (fail_pos - copy) -
(decimal_point_len - 1);
else
fail_pos = (gchar *) nptr + (fail_pos - copy);
}
g_free (copy);
if (endptr)
*endptr = fail_pos;
return val;
}
/**
* e_ascii_dtostr:
* @buffer: A buffer to place the resulting string in
* @buf_len: The length of the buffer.
* @format: The printf-style format to use for the
* code to use for converting.
* @d: The double to convert
*
* Converts a double to a string, using the '.' as
* decimal_point. To format the number you pass in
* a printf-style formating string. Allowed conversion
* specifiers are eEfFgG.
*
* If you want to generates enough precision that converting
* the string back using @g_strtod gives the same machine-number
* (on machines with IEEE compatible 64bit doubles) use the format
* string "%.17g". If you do this it is guaranteed that the size
* of the resulting string will never be larger than
* @G_ASCII_DTOSTR_BUF_SIZE bytes.
*
* Returns: the pointer to the buffer with the converted string
**/
gchar *
e_ascii_dtostr (gchar *buffer,
gint buf_len,
const gchar *format,
gdouble d)
{
struct lconv *locale_data;
const gchar *decimal_point;
gint decimal_point_len;
gchar *p;
gint rest_len;
gchar format_char;
g_return_val_if_fail (buffer != NULL, NULL);
g_return_val_if_fail (format[0] == '%', NULL);
g_return_val_if_fail (strpbrk (format + 1, "'l%") == NULL, NULL);
format_char = format[strlen (format) - 1];
g_return_val_if_fail (format_char == 'e' || format_char == 'E' ||
format_char == 'f' || format_char == 'F' ||
format_char == 'g' || format_char == 'G',
NULL);
if (format[0] != '%')
return NULL;
if (strpbrk (format + 1, "'l%"))
return NULL;
if (!(format_char == 'e' || format_char == 'E' ||
format_char == 'f' || format_char == 'F' ||
format_char == 'g' || format_char == 'G'))
return NULL;
g_snprintf (buffer, buf_len, format, d);
locale_data = localeconv ();
decimal_point = locale_data->decimal_point;
decimal_point_len = strlen (decimal_point);
g_return_val_if_fail (decimal_point_len != 0, NULL);
if (strcmp (decimal_point, ".")) {
p = buffer;
if (*p == '+' || *p == '-')
p++;
while (isdigit ((guchar) * p))
p++;
if (strncmp (p, decimal_point, decimal_point_len) == 0) {
*p = '.';
p++;
if (decimal_point_len > 1) {
rest_len = strlen (p + (decimal_point_len - 1));
memmove (
p, p + (decimal_point_len - 1),
rest_len);
p[rest_len] = 0;
}
}
}
return buffer;
}
/**
* e_builder_get_widget:
* @builder: a #GtkBuilder
* @widget_name: name of a widget in @builder
*
* Gets the widget named @widget_name. Note that this function does not
* increment the reference count of the returned widget. If @widget_name
* could not be found in the @builder<!-- -->'s object tree, a run-time
* warning is emitted since this usually indicates a programming error.
*
* This is a convenience function to work around the awkwardness of
* #GtkBuilder returning #GObject pointers, when the vast majority of
* the time you want a #GtkWidget pointer.
*
* If you need something from @builder other than a #GtkWidget, or you
* want to test for the existence of some widget name without incurring
* a run-time warning, use gtk_builder_get_object().
*
* Returns: the widget named @widget_name, or %NULL
**/
GtkWidget *
e_builder_get_widget (GtkBuilder *builder,
const gchar *widget_name)
{
GObject *object;
g_return_val_if_fail (GTK_IS_BUILDER (builder), NULL);
g_return_val_if_fail (widget_name != NULL, NULL);
object = gtk_builder_get_object (builder, widget_name);
if (object == NULL) {
g_warning ("Could not find widget '%s'", widget_name);
return NULL;
}
return GTK_WIDGET (object);
}
/**
* e_load_ui_builder_definition:
* @builder: a #GtkBuilder
* @basename: basename of the UI definition file
*
* Loads a UI definition into @builder from Evolution's UI directory.
* Failure here is fatal, since the application can't function without
* its UI definitions.
**/
void
e_load_ui_builder_definition (GtkBuilder *builder,
const gchar *basename)
{
gchar *filename;
GError *error = NULL;
g_return_if_fail (GTK_IS_BUILDER (builder));
g_return_if_fail (basename != NULL);
filename = g_build_filename (EVOLUTION_UIDIR, basename, NULL);
gtk_builder_add_from_file (builder, filename, &error);
g_free (filename);
if (error != NULL) {
g_error ("%s: %s", basename, error->message);
g_assert_not_reached ();
}
}