aboutsummaryrefslogtreecommitdiffstats
path: root/shell
ModeNameSize
-rw-r--r--Makefile.am5343logstatsplainblame
-rw-r--r--e-convert-local-mail.c8559logstatsplainblame
-rw-r--r--e-migrate-base-dirs.c19447logstatsplainblame
-rw-r--r--e-shell-backend.c19270logstatsplainblame
-rw-r--r--e-shell-backend.h5025logstatsplainblame
-rw-r--r--e-shell-common.h905logstatsplainblame
-rw-r--r--e-shell-content.c21889logstatsplainblame
-rw-r--r--e-shell-content.h3120logstatsplainblame
-rw-r--r--e-shell-migrate.c6733logstatsplainblame
-rw-r--r--e-shell-migrate.h1105logstatsplainblame
-rw-r--r--e-shell-searchbar.c41645logstatsplainblame
-rw-r--r--e-shell-searchbar.h3686logstatsplainblame
-rw-r--r--e-shell-sidebar.c19188logstatsplainblame
-rw-r--r--e-shell-sidebar.h2939logstatsplainblame
-rw-r--r--e-shell-switcher.c19898logstatsplainblame
-rw-r--r--e-shell-switcher.h2748logstatsplainblame
-rw-r--r--e-shell-taskbar.c13540logstatsplainblame
-rw-r--r--e-shell-taskbar.h2555logstatsplainblame
-rw-r--r--e-shell-utils.c7594logstatsplainblame
-rw-r--r--e-shell-utils.h1324logstatsplainblame
-rw-r--r--e-shell-view.c49872logstatsplainblame
-rw-r--r--e-shell-view.h9741logstatsplainblame
-rw-r--r--e-shell-window-actions.c46464logstatsplainblame
-rw-r--r--e-shell-window-actions.h6187logstatsplainblame
-rw-r--r--e-shell-window-private.c15501logstatsplainblame
-rw-r--r--e-shell-window-private.h3753logstatsplainblame
-rw-r--r--e-shell-window.c47705logstatsplainblame
-rw-r--r--e-shell-window.h5430logstatsplainblame
-rw-r--r--e-shell.c45605logstatsplainblame
-rw-r--r--e-shell.h4587logstatsplainblame
-rw-r--r--es-event.c2942logstatsplainblame
-rw-r--r--es-event.h1937logstatsplainblame
-rw-r--r--evo-version.h.in1066logstatsplainblame
-rw-r--r--evolution-icon.rc109logstatsplainblame
-rw-r--r--evolution-mail.ico19622logstatsplainblame
-rw-r--r--evolution-memos.ico19622logstatsplainblame
-rw-r--r--evolution-tasks.ico19622logstatsplainblame
-rw-r--r--evolution.ico19622logstatsplainblame
-rw-r--r--killev.c4351logstatsplainblame
-rw-r--r--main.c19704logstatsplainblame
-rw-r--r--shell.error.xml1113logstatsplainblame
'n927' href='#n927'>927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362
/*
 * 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:
 *      Chris Lahey <clahey@ximian.com>
 *
 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
 *
 */

#include <config.h>

#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 <time.h>
#include <sys/stat.h>
#include <fcntl.h>

#include <gio/gio.h>
#include <gtk/gtk.h>
#include <glib/gi18n.h>
#include <glib/gstdio.h>
#include <libgnome/gnome-help.h>
#include <libgnome/gnome-init.h>

#ifdef G_OS_WIN32
#include <windows.h>
#endif

#include <libedataserver/e-data-server-util.h>
#include <libedataserver/e-categories.h>
#include "filter/filter-option.h"
#include "e-util.h"
#include "e-util-private.h"

/**
 * e_get_user_data_dir:
 *
 * Returns the base directory for Evolution-specific user data.
 * The string is owned by Evolution and must not be modified or freed.
 *
 * Returns: base directory for user data
 **/
const gchar *
e_get_user_data_dir (void)
{
    static gchar *dirname = NULL;

    if (G_UNLIKELY (dirname == NULL))
        dirname = g_build_filename (
            g_get_home_dir (), ".evolution", NULL);

    return dirname;
}

/**
 * e_get_accels_filename:
 *
 * Returns the name of the user data file containing custom keyboard
 * accelerator specifications.
 *
 * Returns: filename for accelerator specifications
 **/
const gchar *
e_get_accels_filename (void)
{
    static gchar *filename = NULL;

    if (G_UNLIKELY (filename == NULL))
        filename = g_build_filename (
            gnome_user_dir_get (),
            "accels", PACKAGE, NULL);

    return filename;
}

/**
 * e_display_help:
 * @parent: a parent #GtkWindow or %NULL
 * @link_id: help section to present or %NULL
 *
 * Opens the user documentation to the section given by @link_id, or to the
 * table of contents if @link_id is %NULL.  If the user documentation cannot
 * be opened, it presents a dialog describing the error.  The dialog is set
 * as transient to @parent if @parent is non-%NULL.
 **/
void
e_display_help (GtkWindow *parent,
                const gchar *link_id)
{
    GtkWidget *dialog;
    GError *error = NULL;

    if (gnome_help_display ("evolution.xml", link_id, &error))
        return;

    dialog = gtk_message_dialog_new_with_markup (
        parent, GTK_DIALOG_DESTROY_WITH_PARENT,
        GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
        "<big><b>%s</b></big>",
        _("Could not display help for Evolution."));

    gtk_message_dialog_format_secondary_text (
        GTK_MESSAGE_DIALOG (dialog), "%s", error->message);

    gtk_dialog_run (GTK_DIALOG (dialog));

    gtk_widget_destroy (dialog);
    g_error_free (error);
}

/**
 * e_load_ui_definition:
 * @ui_manager: a #GtkUIManager
 * @basename: basename of the UI definition file
 *
 * Loads a UI definition into @ui_manager from Evolution's UI directory.
 * Failure here is fatal, since the application can't function without
 * its UI definitions.
 *
 * Returns: The merge ID for the merged UI.  The merge ID can be used to
 *          unmerge the UI with gtk_ui_manager_remove_ui().
 **/
guint
e_load_ui_definition (GtkUIManager *ui_manager,
                      const gchar *basename)
{
    gchar *filename;
    guint merge_id;
    GError *error = NULL;

    g_return_val_if_fail (GTK_IS_UI_MANAGER (ui_manager), 0);
    g_return_val_if_fail (basename != NULL, 0);

    filename = g_build_filename (EVOLUTION_UIDIR, basename, NULL);
    merge_id = gtk_ui_manager_add_ui_from_file (
        ui_manager, filename, &error);
    g_free (filename);

    if (error != NULL) {
        g_error ("%s: %s", basename, error->message);
        g_assert_not_reached ();
    }

    return merge_id;
}

/**
 * e_action_compare_by_label:
 * @action1: a #GtkAction
 * @action2: a #GtkAction
 *
 * Compares the labels for @action1 and @action2 using g_utf8_collate().
 *
 * Returns: &lt; 0 if @action1 compares before @action2, 0 if they
 *          compare equal, &gt; 0 if @action1 compares after @action2
 **/
gint
e_action_compare_by_label (GtkAction *action1,
                           GtkAction *action2)
{
    gchar *label1;
    gchar *label2;
    gint result;

    /* XXX This is horribly inefficient but will generally only be
     *     used on short lists of actions during UI construction. */

    if (action1 == action2)
        return 0;

    g_object_get (action1, "label", &label1, NULL);
    g_object_get (action2, "label", &label2, NULL);

    result = g_utf8_collate (label1, label2);

    g_free (label1);
    g_free (label2);

    return result;
}

/**
 * e_action_group_remove_all_actions:
 * @action_group: a #GtkActionGroup
 *
 * Removes all actions from the action group.
 **/
void
e_action_group_remove_all_actions (GtkActionGroup *action_group)
{
    GList *list, *iter;

    /* XXX I've proposed this function for inclusion in GTK+.
         *     GtkActionGroup stores actions in an internal hash
         *     table and can do this more efficiently by calling
         *     g_hash_table_remove_all().
         *
         *     http://bugzilla.gnome.org/show_bug.cgi?id=550485 */

    g_return_if_fail (GTK_IS_ACTION_GROUP (action_group));

    list = gtk_action_group_list_actions (action_group);
    for (iter = list; iter != NULL; iter = iter->next)
        gtk_action_group_remove_action (action_group, iter->data);
    g_list_free (list);
}

/**
 * e_str_without_underscores:
 * @s: the string to strip underscores from.
 *
 * Strips underscores from a string in the same way @gtk_label_new_with_mnemonis does.
 * The returned string should be freed.
 */
char *
e_str_without_underscores (const char *s)
{
    char *new_string;
    const char *sp;
    char *dp;

    new_string = g_malloc (strlen (s) + 1);

    dp = new_string;
    for (sp = s; *sp != '\0'; sp ++) {
        if (*sp != '_') {
            *dp = *sp;
            dp ++;
        } else if (sp[1] == '_') {
            /* Translate "__" in "_".  */
            *dp = '_';
            dp ++;
            sp ++;
        }
    }
    *dp = 0;

    return new_string;
}

gint
e_str_compare (gconstpointer x, gconstpointer y)
{
    if (x == NULL || y == NULL) {
        if (x == y)
            return 0;
        else
            return x ? -1 : 1;
    }

    return strcmp (x, y);
}

gint
e_str_case_compare (gconstpointer x, gconstpointer y)
{
    gchar *cx, *cy;
    gint res;

    if (x == NULL || y == NULL) {
        if (x == y)
            return 0;
        else
            return x ? -1 : 1;
    }

    cx = g_utf8_casefold (x, -1);
    cy = g_utf8_casefold (y, -1);

    res = g_utf8_collate (cx, cy);

    g_free (cx);
    g_free (cy);

    return res;
}

gint
e_collate_compare (gconstpointer x, gconstpointer y)
{
    if (x == NULL || y == NULL) {
        if (x == y)
            return 0;
        else
            return x ? -1 : 1;
    }

    return g_utf8_collate (x, y);
}

gint
e_int_compare (gconstpointer x, gconstpointer y)
{
    gint nx = GPOINTER_TO_INT (x);
    gint ny = GPOINTER_TO_INT (y);

    return (nx == ny) ? 0 : (nx < ny) ? -1 : 1;
}

gboolean
e_write_file_uri (const gchar *filename, const gchar *data)
{
    gboolean res;
    gsize length;
    GFile *file;
    GOutputStream *stream;
    GError *error = NULL;

    g_return_val_if_fail (filename != NULL, FALSE);
    g_return_val_if_fail (data != NULL, FALSE);

    length = strlen (data);

    /* if it is uri, then create file for uri, otherwise for path */
    if (strstr (filename, "://"))
        file = g_file_new_for_uri (filename);
    else
        file = g_file_new_for_path (filename);

    if (!file) {
        g_warning ("Couldn't save item");
        return FALSE;
    }

    stream = G_OUTPUT_STREAM (g_file_replace (file, NULL, FALSE, G_FILE_CREATE_NONE, NULL, &error));
    g_object_unref (file);

    if (!stream || error) {
        g_warning ("Couldn't save item%s%s", error ? ": " : "", error ? error->message : "");

        if (stream)
            g_object_unref (stream);

        if (error)
            g_error_free (error);

        return FALSE;
    }

    res = g_output_stream_write_all (stream, data, length, NULL, NULL, &error);

    if (error) {
        g_warning ("Couldn't save item: %s", error->message);
        g_clear_error (&error);
    }

    g_output_stream_close (stream, NULL, &error);
    g_object_unref (stream);

    if (error) {
        g_warning ("Couldn't close output stream: %s", error->message);
        g_error_free (error);
    }

    return res;
}

static gint
epow10 (gint number)
{
    gint value = 1;

    while (number-- > 0)
        value *= 10;

    return value;
}

gchar *
e_format_number (gint number)
{
    GList *iterator, *list = NULL;
    struct lconv *locality;
    gint char_length = 0;
    gint group_count = 0;
    gchar *grouping;
    gint last_count = 3;
    gint divider;
    gchar *value;
    gchar *value_iterator;

    locality = localeconv();
    grouping = locality->grouping;
    while (number) {
        gchar *group;
        switch (*grouping) {
        default:
            last_count = *grouping;
            grouping++;
        case 0:
            divider = epow10(last_count);
            if (number >= divider) {
                group = g_strdup_printf("%0*d", last_count, number % divider);
            } else {
                group = g_strdup_printf("%d", number % divider);
            }
            number /= divider;
            break;
        case CHAR_MAX:
            group = g_strdup_printf("%d", number);
            number = 0;
            break;
        }
        char_length += strlen(group);
        list = g_list_prepend(list, group);
        group_count ++;
    }

    if (list) {
        value = g_new(gchar, 1 + char_length + (group_count - 1) * strlen(locality->thousands_sep));

        iterator = list;
        value_iterator = value;

        strcpy(value_iterator, iterator->data);
        value_iterator += strlen(iterator->data);
        for (iterator = iterator->next; iterator; iterator = iterator->next) {
            strcpy(value_iterator, locality->thousands_sep);
            value_iterator += strlen(locality->thousands_sep);

            strcpy(value_iterator, iterator->data);
            value_iterator += strlen(iterator->data);
        }
        g_list_foreach (list, (GFunc) g_free, NULL);
        g_list_free (list);
        return value;
    } else {
        return g_strdup("0");
    }
}

/* Perform a binary search for key in base which has nmemb elements
   of size bytes each.  The comparisons are done by (*compare)().  */
void
e_bsearch (gconstpointer key,
           gconstpointer base,
           gsize nmemb,
           gsize size,
       ESortCompareFunc compare,
           gpointer closure,
           gsize *start,
           gsize *end)
{
    gsize l, u, idx;
    gconstpointer p;
    gint comparison;
    if (!(start || end))
        return;

    l = 0;
    u = nmemb;
    while (l < u) {
        idx = (l + u) / 2;
        p = (((const gchar *) base) + (idx * size));
        comparison = (*compare) (key, p, closure);
        if (comparison < 0)
            u = idx;
        else if (comparison > 0)
            l = idx + 1;
        else {
            gsize lsave, usave;
            lsave = l;
            usave = u;
            if (start) {
                while (l < u) {
                    idx = (l + u) / 2;
                    p = (((const gchar *) base) + (idx * size));
                    comparison = (*compare) (key, p, closure);
                    if (comparison <= 0)
                        u = idx;
                    else
                        l = idx + 1;
                }
                *start = l;

                l = lsave;
                u = usave;
            }
            if (end) {
                while (l < u) {
                    idx = (l + u) / 2;
                    p = (((const gchar *) base) + (idx * size));
                    comparison = (*compare) (key, p, closure);
                    if (comparison < 0)
                        u = idx;
                    else
                        l = idx + 1;
                }
                *end = l;
            }
            return;
        }
    }

    if (start)
        *start = l;
    if (end)
        *end = l;
}

/**
 * Function to do a last minute fixup of the AM/PM stuff if the locale
 * and gettext haven't done it right. Most English speaking countries
 * except the USA use the 24 hour clock (UK, Australia etc). However
 * since they are English nobody bothers to write a language
 * translation (gettext) file. So the locale turns off the AM/PM, but
 * gettext does not turn on the 24 hour clock. Leaving a mess.
 *
 * This routine checks if AM/PM are defined in the locale, if not it
 * forces the use of the 24 hour clock.
 *
 * The function itself is a front end on strftime and takes exactly
 * the same arguments.
 *
 * TODO: Actually remove the '%p' from the fixed up string so that
 * there isn't a stray space.
 **/

gsize
e_strftime_fix_am_pm (gchar *str, gsize max, const gchar *fmt,
                      const struct tm *tm)
{
    gchar buf[10];
    gchar *sp;
    gchar *ffmt;
    gsize ret;

    if (strstr(fmt, "%p")==NULL && strstr(fmt, "%P")==NULL) {
        /* No AM/PM involved - can use the fmt string directly */
        ret=e_strftime(str, max, fmt, tm);
    } else {
        /* Get the AM/PM symbol from the locale */
        e_strftime (buf, 10, "%p", tm);

        if (buf[0]) {
            /**
             * AM/PM have been defined in the locale
             * so we can use the fmt string directly
             **/
            ret=e_strftime(str, max, fmt, tm);
        } else {
            /**
             * No AM/PM defined by locale
             * must change to 24 hour clock
             **/
            ffmt=g_strdup(fmt);
            for (sp=ffmt; (sp=strstr(sp, "%l")); sp++) {
                /**
                 * Maybe this should be 'k', but I have never
                 * seen a 24 clock actually use that format
                 **/
                sp[1]='H';
            }
            for (sp=ffmt; (sp=strstr(sp, "%I")); sp++) {
                sp[1]='H';
            }
            ret=e_strftime(str, max, ffmt, tm);
            g_free(ffmt);
        }
    }

    return(ret);
}

gsize
e_utf8_strftime_fix_am_pm (gchar *str, gsize max, const gchar *fmt,
                           const struct tm *tm)
{
    gsize sz, ret;
    gchar *locale_fmt, *buf;

    locale_fmt = g_locale_from_utf8(fmt, -1, NULL, &sz, NULL);
    if (!locale_fmt)
        return 0;

    ret = e_strftime_fix_am_pm(str, max, locale_fmt, tm);
    if (!ret) {
        g_free (locale_fmt);
        return 0;
    }

    buf = g_locale_to_utf8(str, ret, NULL, &sz, NULL);
    if (!buf) {
        g_free (locale_fmt);
        return 0;
    }

    if (sz >= max) {
        gchar *tmp = buf + max - 1;
        tmp = g_utf8_find_prev_char(buf, tmp);
        if (tmp)
            sz = tmp - buf;
        else
            sz = 0;
    }
    memcpy(str, buf, sz);
    str[sz] = '\0';
    g_free(locale_fmt);
    g_free(buf);
    return sz;
}

/**
 * e_get_month_name:
 * @month: month index
 * @abbreviated: if %TRUE, abbreviate the month name
 *
 * Returns the localized name for @month.  If @abbreviated is %TRUE,
 * returns the locale's abbreviated month name.
 *
 * Returns: localized month name
 **/
const gchar *
e_get_month_name (GDateMonth month,
                  gboolean abbreviated)
{
    /* Make the indices correspond to the enum values. */
    static const gchar *abbr_names[G_DATE_DECEMBER + 1];
    static const gchar *full_names[G_DATE_DECEMBER + 1];
    static gboolean first_time = TRUE;

    g_return_val_if_fail (month >= G_DATE_JANUARY, NULL);
    g_return_val_if_fail (month <= G_DATE_DECEMBER, NULL);

    if (G_UNLIKELY (first_time)) {
        gchar buffer[256];
        GDateMonth ii;
        GDate date;

        memset (abbr_names, 0, sizeof (abbr_names));
        memset (full_names, 0, sizeof (full_names));

        /* First Julian day was in January. */
        g_date_set_julian (&date, 1);

        for (ii = G_DATE_JANUARY; ii <= G_DATE_DECEMBER; ii++) {
            g_date_strftime (buffer, sizeof (buffer), "%b", &date);
            abbr_names[ii] = g_intern_string (buffer);
            g_date_strftime (buffer, sizeof (buffer), "%B", &date);
            full_names[ii] = g_intern_string (buffer);
            g_date_add_months (&date, 1);
        }

        first_time = FALSE;
    }

    return abbreviated ? abbr_names[month] : full_names[month];
}

/**
 * e_get_weekday_name:
 * @weekday: weekday index
 * @abbreviated: if %TRUE, abbreviate the weekday name
 *
 * Returns the localized name for @weekday.  If @abbreviated is %TRUE,
 * returns the locale's abbreviated weekday name.
 *
 * Returns: localized weekday name
 **/
const gchar *
e_get_weekday_name (GDateWeekday weekday,
                    gboolean abbreviated)
{
    /* Make the indices correspond to the enum values. */
    static const gchar *abbr_names[G_DATE_SUNDAY + 1];
    static const gchar *full_names[G_DATE_SUNDAY + 1];
    static gboolean first_time = TRUE;

    g_return_val_if_fail (weekday >= G_DATE_MONDAY, NULL);
    g_return_val_if_fail (weekday <= G_DATE_SUNDAY, NULL);

    if (G_UNLIKELY (first_time)) {
        gchar buffer[256];
        GDateWeekday ii;
        GDate date;

        memset (abbr_names, 0, sizeof (abbr_names));
        memset (full_names, 0, sizeof (full_names));

        /* First Julian day was a Monday. */
        g_date_set_julian (&date, 1);

        for (ii = G_DATE_MONDAY; ii <= G_DATE_SUNDAY; ii++) {
            g_date_strftime (buffer, sizeof (buffer), "%a", &date);
            abbr_names[ii] = g_intern_string (buffer);
            g_date_strftime (buffer, sizeof (buffer), "%A", &date);
            full_names[ii] = g_intern_string (buffer);
            g_date_add_days (&date, 1);
        }

        first_time = FALSE;
    }

    return abbreviated ? abbr_names[weekday] : full_names[weekday];
}

/**
 * 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.
 *
 * Return value: 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.
 *
 * Return value: 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;
}

gchar *
e_strdup_append_strings (gchar *first_string, ...)
{
    gchar *buffer;
    gchar *current;
    gint length;
    va_list args1;
    va_list args2;
    gchar *v_string;
    gint v_int;

    va_start (args1, first_string);
    G_VA_COPY (args2, args1);

    length = 0;

    v_string = first_string;
    while (v_string) {
        v_int = va_arg (args1, gint);
        if (v_int >= 0)
            length += v_int;
        else
            length += strlen (v_string);
        v_string = va_arg (args1, gchar *);
    }

    buffer  = g_new (gchar, length + 1);
    current = buffer;

    v_string = first_string;
    while (v_string) {
        v_int = va_arg (args2, gint);
        if (v_int < 0) {
            gint i;
            for (i = 0; v_string[i]; i++) {
                *(current++) = v_string[i];
            }
        } else {
            gint i;
            for (i = 0; v_string[i] && i < v_int; i++) {
                *(current++) = v_string[i];
            }
        }
        v_string = va_arg (args2, gchar *);
    }
    *(current++) = 0;

    va_end (args1);
    va_end (args2);

    return buffer;
}

cairo_font_options_t *
get_font_options (void)
{
    gchar *antialiasing, *hinting, *subpixel_order;
    GConfClient *gconf = gconf_client_get_default ();
    cairo_font_options_t *font_options = cairo_font_options_create ();

    /* Antialiasing */
    antialiasing = gconf_client_get_string (gconf,
            "/desktop/gnome/font_rendering/antialiasing", NULL);
    if (antialiasing == NULL)
        cairo_font_options_set_antialias (font_options, CAIRO_ANTIALIAS_DEFAULT);
    else {
        if (strcmp (antialiasing, "grayscale") == 0)
            cairo_font_options_set_antialias (font_options, CAIRO_ANTIALIAS_GRAY);
        else if (strcmp (antialiasing, "rgba") == 0)
            cairo_font_options_set_antialias (font_options, CAIRO_ANTIALIAS_SUBPIXEL);
        else if (strcmp (antialiasing, "none") == 0)
            cairo_font_options_set_antialias (font_options, CAIRO_ANTIALIAS_NONE);
        else
            cairo_font_options_set_antialias (font_options, CAIRO_ANTIALIAS_DEFAULT);
    }
    hinting = gconf_client_get_string (gconf,
            "/desktop/gnome/font_rendering/hinting", NULL);
    if (hinting == NULL)
        cairo_font_options_set_hint_style (font_options, CAIRO_HINT_STYLE_DEFAULT);
    else {
        if (strcmp (hinting, "full") == 0)
            cairo_font_options_set_hint_style (font_options, CAIRO_HINT_STYLE_FULL);
        else if (strcmp (hinting, "medium") == 0)
            cairo_font_options_set_hint_style (font_options, CAIRO_HINT_STYLE_MEDIUM);
        else if (strcmp (hinting, "slight") == 0)
            cairo_font_options_set_hint_style (font_options, CAIRO_HINT_STYLE_SLIGHT);
        else if (strcmp (hinting, "none") == 0)
            cairo_font_options_set_hint_style (font_options, CAIRO_HINT_STYLE_NONE);
        else
            cairo_font_options_set_hint_style (font_options, CAIRO_HINT_STYLE_DEFAULT);
    }
    subpixel_order = gconf_client_get_string (gconf,
            "/desktop/gnome/font_rendering/rgba_order", NULL);
    if (subpixel_order == NULL)
        cairo_font_options_set_subpixel_order (font_options, CAIRO_SUBPIXEL_ORDER_DEFAULT);
    else {
        if (strcmp (subpixel_order, "rgb") == 0)
            cairo_font_options_set_subpixel_order (font_options, CAIRO_SUBPIXEL_ORDER_RGB);
        else if (strcmp (subpixel_order, "bgr") == 0)
            cairo_font_options_set_subpixel_order (font_options, CAIRO_SUBPIXEL_ORDER_BGR);
        else if (strcmp (subpixel_order, "vrgb") == 0)
            cairo_font_options_set_subpixel_order (font_options, CAIRO_SUBPIXEL_ORDER_VRGB);
        else if (strcmp (subpixel_order, "vbgr") == 0)
            cairo_font_options_set_subpixel_order (font_options, CAIRO_SUBPIXEL_ORDER_VBGR);
        else
            cairo_font_options_set_subpixel_order (font_options, CAIRO_SUBPIXEL_ORDER_DEFAULT);
    }
    g_free (antialiasing);
    g_free (hinting);
    g_free (subpixel_order);
    g_object_unref (gconf);
    return font_options;
}

/**
 * e_file_update_save_path:
 * @uri: URI to store
 * @free: If TRUE, free uri
 *
 * Save the save_dir path for evolution.  If free is TRUE, uri gets freed when
 * done.  Genearally, this should be called with the output of
 * gtk_file_chooser_get_current_folder_uri()  The URI must be a path URI, not a
 * file URI.
 **/
void
e_file_update_save_path (gchar *uri, gboolean free)
{
    GConfClient *gconf = gconf_client_get_default();
    GError *error = NULL;

    gconf_client_set_string(gconf, "/apps/evolution/mail/save_dir", uri, &error);
    if (error != NULL) {
        g_warning("%s (%s) %s", G_STRLOC, G_STRFUNC, error->message);
        g_clear_error(&error);
    }
    g_object_unref(gconf);
    if (free)
        g_free(uri);
}

/**
 * e_file_get_save_path:
 *
 * Return the save_dir path for evolution.  If there isn't a save_dir, returns
 * the users home directory.  Returns an allocated URI that should be freed by
 * the caller.
 **/
gchar *
e_file_get_save_path (void)
{
    GConfClient *gconf = gconf_client_get_default();
    GError *error = NULL;
    gchar *uri;

    uri = gconf_client_get_string(gconf, "/apps/evolution/mail/save_dir", &error);
    if (error != NULL) {
        g_warning("%s (%s) %s", G_STRLOC, G_STRFUNC, error->message);
        g_clear_error(&error);
    }
    g_object_unref(gconf);

    if (uri == NULL) {
        GFile *file;

        file = g_file_new_for_path (g_get_home_dir ());
        if (file) {
            uri = g_file_get_uri (file);
            g_object_unref (file);
        }
    }

    return (uri);
}

/* Evolution Locks for crash recovery */

#define LOCK_FILE ".running"

static const gchar *
get_lock_filename (void)
{
    static gchar *filename = NULL;

    if (G_UNLIKELY (filename == NULL))
        filename = g_build_filename (e_get_user_data_dir (), LOCK_FILE, NULL);

    return filename;
}

gboolean
e_file_lock_create ()
{
    const char *fname = get_lock_filename ();
    gboolean status = FALSE;

    int fd = g_creat (fname, S_IRUSR|S_IWUSR);
    if (fd == -1){
        g_warning ("Lock file '%s' creation failed, error %d\n", fname, errno);
    } else {
        status = TRUE;
        close (fd);
    }

    return status;
}

void
e_file_lock_destroy ()
{
    const char *fname = get_lock_filename ();

    if (g_unlink (fname) == -1){
        g_warning ("Lock destroy: failed to unlink file '%s'!",fname);
    }
}

gboolean 
e_file_lock_exists ()
{
    const char *fname = get_lock_filename ();

    return g_file_test (fname, G_FILE_TEST_EXISTS);
}

/**
 * e_util_guess_mime_type:
 * @filename: it's a local file name, or URI.
 * @localfile: set to TRUE if can check the local file content, FALSE to check only based on the filename itself.
 * Returns: NULL or newly allocated string with a mime_type of the given file. Free with g_free.
 *
 * Guesses mime_type for the given filename.
 **/
char *
e_util_guess_mime_type (const char *filename, gboolean localfile)
{
    char *mime_type = NULL;

    g_return_val_if_fail (filename != NULL, NULL);

    if (localfile) {
        GFile *file;

        if (strstr (filename, "://"))
            file = g_file_new_for_uri (filename);
        else
            file = g_file_new_for_path (filename);

        if (file) {
            GFileInfo *fi;

            fi = g_file_query_info (file, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE, G_FILE_QUERY_INFO_NONE, NULL, NULL);
            if (fi) {
                mime_type = g_content_type_get_mime_type (g_file_info_get_content_type (fi));
    
                g_object_unref (fi);
            }

            g_object_unref (file);
        }
    }

    if (!mime_type) {
        /* file doesn't exists locally, thus guess based on the filename */
        gboolean uncertain = FALSE;
        gchar *content_type;

        content_type = g_content_type_guess (filename, NULL, 0, &uncertain);
        if (content_type) {
            mime_type = g_content_type_get_mime_type (content_type);
            g_free (content_type);
        }
    }

    return mime_type;
}

/**
 * e_util_filename_to_uri:
 * @filename: local file name.
 * Returns: either newly allocated string or NULL. Free with g_free.
 *
 * Converts local file name to URI.
 **/
char *
e_util_filename_to_uri (const char *filename)
{
    GFile *file;
    char *uri = NULL;

    g_return_val_if_fail (filename != NULL, NULL);

    file = g_file_new_for_path (filename);

    if (file) {
        uri = g_file_get_uri (file);
        g_object_unref (file);
    }

    return uri;
}

/**
 * e_util_uri_to_filename:
 * @uri: uri.
 * Returns: either newly allocated string or NULL. Free with g_free.
 *
 * Converts URI to local file name. NULL indicates no such local file name exists.
 **/
char *
e_util_uri_to_filename (const char *uri)
{
    GFile *file;
    char *filename = NULL;

    g_return_val_if_fail (uri != NULL, NULL);

    file = g_file_new_for_uri (uri);

    if (file) {
        filename = g_file_get_path (file);
        g_object_unref (file);
    }

    return filename;
}

/**
 * e_util_read_file:
 * @filename: File name to read.
 * @filename_is_uri: Whether the file name is URI, if not, then it's a local path.
 * @buffer: Read content or the file. Should not be NULL. Returned value should be freed with g_free.
 * @read: Number of actually read bytes. Should not be NULL.
 * @error: Here will be returned an error from reading operations. Can be NULL. Not every time is set when returned FALSE.
 * Returns: Whether was reading successful or not.
 *
 * Reads synchronously content of the file, to which is pointed either by path or by URI.
 * Mount point should be already mounted when calling this function.
 **/
gboolean
e_util_read_file (const char *filename, gboolean filename_is_uri, char **buffer, gsize *read, GError **error)
{
    GFile *file;
    GFileInfo *info;
    GError *err = NULL;
    gboolean res = FALSE;

    g_return_val_if_fail (filename != NULL, FALSE);
    g_return_val_if_fail (buffer != NULL, FALSE);
    g_return_val_if_fail (read != NULL, FALSE);

    *buffer = NULL;
    *read = 0;

    if (filename_is_uri)
        file = g_file_new_for_uri (filename);
    else
        file = g_file_new_for_path (filename);

    g_return_val_if_fail (file != NULL, FALSE);

    info = g_file_query_info (file, G_FILE_ATTRIBUTE_STANDARD_SIZE, G_FILE_QUERY_INFO_NONE, NULL, &err);

    if (!err && info) {
        guint64 sz;
        char *buff;

        sz = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_STANDARD_SIZE);
        buff = g_malloc (sizeof (char) * sz);

        if (buff) {
            GInputStream *stream;

            stream = G_INPUT_STREAM (g_file_read (file, NULL, &err));

            if (!err && stream) {
                res = g_input_stream_read_all (stream, buff, sz, read, NULL, &err);

                if (err)
                    res = FALSE;
                
                if (res)
                    *buffer = buff;
                else
                    g_free (buff);
            }

            if (stream)
                g_object_unref (stream);
        }
    }

    if (info)
        g_object_unref (info);

    g_object_unref (file);

    if (err) {
        if (error)
            *error = err;
        else
            g_error_free (err);
    }

    return res;
}

GSList *
e_util_get_category_filter_options (void)
{
    GSList *res = NULL;
    GList *clist, *l;

    clist = e_categories_get_list ();
    for (l = clist; l; l = l->next) {
        const char *cname = l->data;
        struct _filter_option *fo = g_new0 (struct _filter_option, 1);

        fo->title = g_strdup (cname);
        fo->value = g_strdup (cname);
        res = g_slist_prepend (res, fo);
    }

    g_list_free (clist);

    return g_slist_reverse (res);
}