2007-01-21 [colin] 2.7.1cvs44
[claws.git] / src / common / utils.c
index c0d8b6578cbad2b8201389ee805387a44d46a2cc..c1d1d84c15c4cc67c3358f771905e2c5254b8cbd 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
- * Copyright (C) 1999-2006 Hiroyuki Yamamoto & The Sylpheed-Claws Team
+ * Copyright (C) 1999-2007 Hiroyuki Yamamoto & The Claws Mail Team
  *
  * 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
 #include "defs.h"
 
 #include <glib.h>
-#ifdef ENABLE_NLS
+
 #include <glib/gi18n.h>
-#else
-#define _(a) (a)
-#define N_(a) (a)
-#endif
+
 #include <stdio.h>
 #include <string.h>
 #include <ctype.h>
@@ -336,13 +333,9 @@ gchar *itos(gint n)
 
 #define divide(num,divisor,i,d)                \
 {                                      \
-       register int tmp;               \
-       i = num/divisor;                \
-       tmp = i*divisor;                \
-       d = num-tmp;                    \
-       if (d > 1000) d /= 1000;        \
-       else if (d > 100) d /= 100;     \
-       else if (d > 10) d /= 10;               \
+       i = num >> divisor;             \
+       d = num & ((1<<divisor)-1);     \
+       d = (d*100) >> divisor;         \
 }
 
 gchar *to_human_readable(off_t size)
@@ -353,20 +346,21 @@ gchar *to_human_readable(off_t size)
        register int t = 0, r = 0;
        if (b_format == NULL) {
                b_format  = _("%dB");
-               kb_format = _("%d.%dKB");
-               mb_format = _("%.2fMB");
+               kb_format = _("%d.%02dKB");
+               mb_format = _("%d.%02dMB");
                gb_format = _("%.2fGB");
        }
        
-       if (size < 1024) {
+       if (size < (off_t)1024) {
                g_snprintf(str, sizeof(str), b_format, (gint)size);
                return str;
-       } else if (size >> 10 < 1024) {
-               divide(size, (1 << 10), t, r);
+       } else if (size >> 10 < (off_t)1024) {
+               divide(size, 10, t, r);
                g_snprintf(str, sizeof(str), kb_format, t, r);
                return str;
-       } else if (size >> 20 < 1024) {
-               g_snprintf(str, sizeof(str), mb_format, (gfloat)size / (1 << 20));
+       } else if (size >> 20 < (off_t)1024) {
+               divide(size, 20, t, r);
+               g_snprintf(str, sizeof(str), mb_format, t, r);
                return str;
        } else {
                g_snprintf(str, sizeof(str), gb_format, (gfloat)size / (1 << 30));
@@ -1645,10 +1639,14 @@ void decode_uri(gchar *decoded_uri, const gchar *encoded_uri)
 }
 
 gint scan_mailto_url(const gchar *mailto, gchar **to, gchar **cc, gchar **bcc,
-                    gchar **subject, gchar **body)
+                    gchar **subject, gchar **body, gchar **attach)
 {
        gchar *tmp_mailto;
        gchar *p;
+       const gchar *forbidden_uris[] = { ".gnupg/",
+                                         "/etc/passwd",
+                                         "/etc/shadow",
+                                         NULL };
 
        Xstrdup_a(tmp_mailto, mailto, return -1);
 
@@ -1695,6 +1693,19 @@ gint scan_mailto_url(const gchar *mailto, gchar **to, gchar **cc, gchar **bcc,
                } else if (body && !*body && !g_ascii_strcasecmp(field, "body")) {
                        *body = g_malloc(strlen(value) + 1);
                        decode_uri(*body, value);
+               } else if (attach && !*attach && !g_ascii_strcasecmp(field, "attach")) {
+                       int i = 0;
+                       *attach = g_malloc(strlen(value) + 1);
+                       decode_uri(*attach, value);
+                       for (; forbidden_uris[i]; i++) {
+                               if (strstr(*attach, forbidden_uris[i])) {
+                                       printf("Refusing to attach '%s', potential private data leak\n",
+                                                       *attach);
+                                       g_free(*attach);
+                                       *attach = NULL;
+                                       break;
+                               }
+                       }
                }
        }
 
@@ -1973,7 +1984,7 @@ const gchar *get_plugin_dir(void)
 
        if (!plugin_dir)
                plugin_dir = g_strconcat(w32_get_module_dir(),
-                                        "\\lib\\sylpheed-claws\\plugins\\",
+                                        "\\lib\\claws-mail\\plugins\\",
                                         NULL);
        return plugin_dir;
 #else
@@ -2054,6 +2065,18 @@ off_t get_file_size(const gchar *file)
        return s.st_size;
 }
 
+time_t get_file_mtime(const gchar *file)
+{
+       struct stat s;
+
+       if (g_stat(file, &s) < 0) {
+               FILE_OP_ERROR(file, "stat");
+               return -1;
+       }
+
+       return s.st_mtime;
+}
+
 off_t get_file_size_as_crlf(const gchar *file)
 {
        FILE *fp;
@@ -3097,7 +3120,7 @@ FILE *my_tmpfile(void)
        tmplen = strlen(tmpdir);
        progname = g_get_prgname();
        if (progname == NULL)
-               progname = "sylpheed-claws";
+               progname = "claws-mail";
        proglen = strlen(progname);
        Xalloca(fname, tmplen + 1 + proglen + sizeof(suffix),
                return tmpfile());
@@ -3139,12 +3162,12 @@ FILE *get_tmpfile_in_dir(const gchar *dir, gchar **filename)
 {
        int fd;
 #ifdef G_OS_WIN32
-       char *template = g_strdup_printf ("%s%csylpheed.XXXXXX",
+       char *template = g_strdup_printf ("%s%cclaws.XXXXXX",
                                          dir, G_DIR_SEPARATOR);
        fd = mkstemp_name(template, filename);
        g_free(template);
 #else
-       *filename = g_strdup_printf("%s%csylpheed.XXXXXX", dir, G_DIR_SEPARATOR);
+       *filename = g_strdup_printf("%s%cclaws.XXXXXX", dir, G_DIR_SEPARATOR);
        fd = mkstemp(*filename);
 #endif
        return fdopen(fd, "w+");
@@ -3271,7 +3294,7 @@ gchar *file_read_stream_to_str(FILE *fp)
        return str;
 }
 
-gint execute_async(gchar *const argv[])
+static gint execute_async(gchar *const argv[])
 {
        g_return_val_if_fail(argv != NULL && argv[0] != NULL, -1);
 
@@ -3284,7 +3307,7 @@ gint execute_async(gchar *const argv[])
        return 0;
 }
 
-gint execute_sync(gchar *const argv[])
+static gint execute_sync(gchar *const argv[])
 {
        gint status;
 
@@ -3355,7 +3378,7 @@ static gint is_unchanged_uri_char(char c)
        }
 }
 
-void encode_uri(gchar *encoded_uri, gint bufsize, const gchar *uri)
+static void encode_uri(gchar *encoded_uri, gint bufsize, const gchar *uri)
 {
        int i;
        int k;
@@ -4526,9 +4549,9 @@ static gchar *mailcap_get_command_in_file(const gchar *path, const gchar *type,
        while (fgets(buf, sizeof (buf), fp) != NULL) {
                gchar **parts = g_strsplit(buf, ";", 3);
                gchar *trimmed = parts[0];
-               while (trimmed[0] == ' ')
+               while (trimmed[0] == ' ' || trimmed[0] == '\t')
                        trimmed++;
-               while (trimmed[strlen(trimmed)-1] == ' ')
+               while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
                        trimmed[strlen(trimmed)-1] = '\0';
 
                if (!strcmp(trimmed, type)) {
@@ -4541,13 +4564,13 @@ static gchar *mailcap_get_command_in_file(const gchar *path, const gchar *type,
                                gchar *testcmd = orig_testcmd;
                                if (strstr(testcmd,";"))
                                        *(strstr(testcmd,";")) = '\0';
-                               while (testcmd[0] == ' ')
+                               while (testcmd[0] == ' ' || testcmd[0] == '\t')
                                        testcmd++;
                                while (testcmd[strlen(testcmd)-1] == '\n')
                                        testcmd[strlen(testcmd)-1] = '\0';
                                while (testcmd[strlen(testcmd)-1] == '\r')
                                        testcmd[strlen(testcmd)-1] = '\0';
-                               while (testcmd[strlen(testcmd)-1] == ' ')
+                               while (testcmd[strlen(testcmd)-1] == ' ' || testcmd[strlen(testcmd)-1] == '\t')
                                        testcmd[strlen(testcmd)-1] = '\0';
                                        
                                if (strstr(testcmd, "%s")) {
@@ -4572,13 +4595,13 @@ static gchar *mailcap_get_command_in_file(const gchar *path, const gchar *type,
                        }
                        
                        trimmed = parts[1];
-                       while (trimmed[0] == ' ')
+                       while (trimmed[0] == ' ' || trimmed[0] == '\t')
                                trimmed++;
                        while (trimmed[strlen(trimmed)-1] == '\n')
                                trimmed[strlen(trimmed)-1] = '\0';
                        while (trimmed[strlen(trimmed)-1] == '\r')
                                trimmed[strlen(trimmed)-1] = '\0';
-                       while (trimmed[strlen(trimmed)-1] == ' ')
+                       while (trimmed[strlen(trimmed)-1] == ' ' || trimmed[strlen(trimmed)-1] == '\t')
                                trimmed[strlen(trimmed)-1] = '\0';
                        result = g_strdup(trimmed);
                        g_strfreev(parts);
@@ -4635,18 +4658,13 @@ void mailcap_update_default(const gchar *type, const gchar *command)
        FILE *outfp = fopen(outpath, "wb");
        gchar buf[BUFFSIZE];
 
-       if (!fp) {
-               g_free(path);
-               g_free(outpath);
-               return;
-       }
        if (!outfp) {
                g_free(path);
                g_free(outpath);
                fclose(fp);
                return;
        }
-       while (fgets(buf, sizeof (buf), fp) != NULL) {
+       while (fp && fgets(buf, sizeof (buf), fp) != NULL) {
                gchar **parts = g_strsplit(buf, ";", 3);
                gchar *trimmed = parts[0];
                while (trimmed[0] == ' ')
@@ -4664,7 +4682,10 @@ void mailcap_update_default(const gchar *type, const gchar *command)
                g_strfreev(parts);
        }
        fprintf(outfp, "%s; %s\n", type, command);
-       fclose(fp);
+
+       if (fp)
+               fclose(fp);
+
        fclose(outfp);
        g_rename(outpath, path);
 }
@@ -4689,14 +4710,30 @@ gint copy_dir(const gchar *src, const gchar *dst)
                debug_print("copying: %s -> %s\n", old_file, new_file);
                if (g_file_test(old_file, G_FILE_TEST_IS_REGULAR)) {
                        gint r = copy_file(old_file, new_file, TRUE);
-                       if (r < 0)
+                       if (r < 0) {
+                               g_dir_close(dir);
                                return r;
+                       }
+               } else if (g_file_test(old_file, G_FILE_TEST_IS_SYMLINK)) {
+                       GError *error;
+                       gint r = 0;
+                       gchar *target = g_file_read_link(old_file, &error);
+                       if (target)
+                               r = symlink(target, new_file);
+                       g_free(target);
+                       if (r < 0) {
+                               g_dir_close(dir);
+                               return r;
+                       }
                } else if (g_file_test(old_file, G_FILE_TEST_IS_DIR)) {
                        gint r = copy_dir(old_file, new_file);
-                       if (r < 0)
+                       if (r < 0) {
+                               g_dir_close(dir);
                                return r;
+                       }
                }
        }
+       g_dir_close(dir);
        return 0;
 }
 
@@ -4746,3 +4783,360 @@ gboolean sc_g_slist_bigger(GSList *list, gint max)
        }
        return (i > max);
 }
+
+const gchar *daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
+const gchar *monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL, 
+                            NULL, NULL, NULL, NULL, NULL, NULL};
+const gchar *s_daynames[] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL};
+const gchar *s_monthnames[] = {NULL, NULL, NULL, NULL, NULL, NULL, 
+                            NULL, NULL, NULL, NULL, NULL, NULL};
+const gchar *s_am_up = NULL;
+const gchar *s_pm_up = NULL;
+const gchar *s_am_low = NULL;
+const gchar *s_pm_low = NULL;
+const gchar *def_loc_format = NULL;
+const gchar *date_loc_format = NULL;
+const gchar *time_loc_format = NULL;
+const gchar *time_am_pm = NULL;
+
+static gboolean time_names_init_done = FALSE;
+
+static void init_time_names(void)
+{
+       daynames[0] = Q_("Complete day name for use by strftime|Sunday");
+       daynames[1] = Q_("Complete day name for use by strftime|Monday");
+       daynames[2] = Q_("Complete day name for use by strftime|Tuesday");
+       daynames[3] = Q_("Complete day name for use by strftime|Wednesday");
+       daynames[4] = Q_("Complete day name for use by strftime|Thursday");
+       daynames[5] = Q_("Complete day name for use by strftime|Friday");
+       daynames[6] = Q_("Complete day name for use by strftime|Saturday");
+       
+       monthnames[0] = Q_("Complete month name for use by strftime|January");
+       monthnames[1] = Q_("Complete month name for use by strftime|February");
+       monthnames[2] = Q_("Complete month name for use by strftime|March");
+       monthnames[3] = Q_("Complete month name for use by strftime|April");
+       monthnames[4] = Q_("Complete month name for use by strftime|May");
+       monthnames[5] = Q_("Complete month name for use by strftime|June");
+       monthnames[6] = Q_("Complete month name for use by strftime|July");
+       monthnames[7] = Q_("Complete month name for use by strftime|August");
+       monthnames[8] = Q_("Complete month name for use by strftime|September");
+       monthnames[9] = Q_("Complete month name for use by strftime|October");
+       monthnames[10] = Q_("Complete month name for use by strftime|November");
+       monthnames[11] = Q_("Complete month name for use by strftime|December");
+
+       s_daynames[0] = Q_("Abbr. day name for use by strftime|Sun");
+       s_daynames[1] = Q_("Abbr. day name for use by strftime|Mon");
+       s_daynames[2] = Q_("Abbr. day name for use by strftime|Tue");
+       s_daynames[3] = Q_("Abbr. day name for use by strftime|Wed");
+       s_daynames[4] = Q_("Abbr. day name for use by strftime|Thu");
+       s_daynames[5] = Q_("Abbr. day name for use by strftime|Fri");
+       s_daynames[6] = Q_("Abbr. day name for use by strftime|Sat");
+       
+       s_monthnames[0] = Q_("Abbr. month name for use by strftime|Jan");
+       s_monthnames[1] = Q_("Abbr. month name for use by strftime|Feb");
+       s_monthnames[2] = Q_("Abbr. month name for use by strftime|Mar");
+       s_monthnames[3] = Q_("Abbr. month name for use by strftime|Apr");
+       s_monthnames[4] = Q_("Abbr. month name for use by strftime|May");
+       s_monthnames[5] = Q_("Abbr. month name for use by strftime|Jun");
+       s_monthnames[6] = Q_("Abbr. month name for use by strftime|Jul");
+       s_monthnames[7] = Q_("Abbr. month name for use by strftime|Aug");
+       s_monthnames[8] = Q_("Abbr. month name for use by strftime|Sep");
+       s_monthnames[9] = Q_("Abbr. month name for use by strftime|Oct");
+       s_monthnames[10] = Q_("Abbr. month name for use by strftime|Nov");
+       s_monthnames[11] = Q_("Abbr. month name for use by strftime|Dec");
+
+       s_am_up = Q_("For use by strftime (morning)|AM");
+       s_pm_up = Q_("For use by strftime (afternoon)|PM");
+       s_am_low = Q_("For use by strftime (morning, lowercase)|am");
+       s_pm_low = Q_("For use by strftime (afternoon, lowercase)|pm");
+       
+       def_loc_format = Q_("For use by strftime (default date+time format)|%a %b %e %H:%M:%S %Y");
+       date_loc_format = Q_("For use by strftime (default date format)|%m/%d/%y");
+       time_loc_format = Q_("For use by strftime (default time format)|%H:%M:%S");
+
+       time_am_pm = Q_("For use by strftime (default 12-hour time format)|%I:%M:%S %p");
+
+       time_names_init_done = TRUE;
+#if 0
+       {
+       struct tm *lt;
+       time_t now = time(NULL), next;
+       gchar buf_a[1024], buf_b[1024];
+       
+       printf("test strftime:\n");
+       tzset();
+       for (next = now; next < now + 86400*365*2; next++) {
+               lt = localtime(&now);
+               strftime(buf_a, 1024, 
+                       "%a,%A,%b,%B,%c,%C,%d,%D,%e,%F,%G,%g,%h,%H,%I "
+                       "%j,%k,%l,%m,%M,%n,%p,%P,%r,%R,%s,%S,%t,%T,%u,%U,%V"
+                       "%w,%W,%x,%X,%y,%Y,%z,%Z,%%,%EY,%OY", lt);
+               fast_strftime(buf_b, 1024, 
+                       "%a,%A,%b,%B,%c,%C,%d,%D,%e,%F,%G,%g,%h,%H,%I "
+                       "%j,%k,%l,%m,%M,%n,%p,%P,%r,%R,%s,%S,%t,%T,%u,%U,%V"
+                       "%w,%W,%x,%X,%y,%Y,%z,%Z,%%,%EY,%OY", lt);
+               if (strcmp(buf_a, buf_b)) {
+                       printf("diff: \n%s\n%s\n", buf_a, buf_b);
+               }
+       }
+       }
+#endif
+}
+
+#define CHECK_SIZE() {                 \
+       total_done += len;              \
+       if (total_done >= buflen) {     \
+               buf[buflen-1] = '\0';   \
+               return 0;               \
+       }                               \
+}
+
+size_t fast_strftime(gchar *buf, gint buflen, const gchar *format, struct tm *lt)
+{
+       gchar *curpos = buf;
+       gint total_done = 0;
+       gchar subbuf[64], subfmt[64];
+       static time_t last_tzset = (time_t)0;
+       
+       if (!time_names_init_done)
+               init_time_names();
+       
+       if (format == NULL || lt == NULL)
+               return 0;
+               
+       if (last_tzset != time(NULL)) {
+               tzset();
+               last_tzset = time(NULL);
+       }
+       while(*format) {
+               if (*format == '%') {
+                       gint len = 0, tmp = 0;
+                       format++;
+                       switch(*format) {
+                       case '%':
+                               len = 1; CHECK_SIZE();
+                               *curpos = '%';
+                               break;
+                       case 'a':
+                               len = strlen(s_daynames[lt->tm_wday]); CHECK_SIZE();
+                               strncpy2(curpos, s_daynames[lt->tm_wday], buflen - total_done);
+                               break;
+                       case 'A':
+                               len = strlen(daynames[lt->tm_wday]); CHECK_SIZE();
+                               strncpy2(curpos, daynames[lt->tm_wday], buflen - total_done);
+                               break;
+                       case 'b':
+                       case 'h':
+                               len = strlen(s_monthnames[lt->tm_mon]); CHECK_SIZE();
+                               strncpy2(curpos, s_monthnames[lt->tm_mon], buflen - total_done);
+                               break;
+                       case 'B':
+                               len = strlen(monthnames[lt->tm_mon]); CHECK_SIZE();
+                               strncpy2(curpos, monthnames[lt->tm_mon], buflen - total_done);
+                               break;
+                       case 'c':
+                               fast_strftime(subbuf, 64, def_loc_format, lt);
+                               len = strlen(subbuf); CHECK_SIZE();
+                               strncpy2(curpos, subbuf, buflen - total_done);
+                               break;
+                       case 'C':
+                               total_done += 2; CHECK_SIZE();
+                               tmp = (lt->tm_year + 1900)/100;
+                               *curpos++ = '0'+(tmp / 10);
+                               *curpos++ = '0'+(tmp % 10);
+                               break;
+                       case 'd':
+                               total_done += 2; CHECK_SIZE();
+                               *curpos++ = '0'+(lt->tm_mday / 10);
+                               *curpos++ = '0'+(lt->tm_mday % 10);
+                               break;
+                       case 'D':
+                               total_done += 8; CHECK_SIZE();
+                               *curpos++ = '0'+((lt->tm_mon+1) / 10);
+                               *curpos++ = '0'+((lt->tm_mon+1) % 10);
+                               *curpos++ = '/';
+                               *curpos++ = '0'+(lt->tm_mday / 10);
+                               *curpos++ = '0'+(lt->tm_mday % 10);
+                               *curpos++ = '/';
+                               tmp = lt->tm_year%100;
+                               *curpos++ = '0'+(tmp / 10);
+                               *curpos++ = '0'+(tmp % 10);
+                               break;
+                       case 'e':
+                               len = 2; CHECK_SIZE();
+                               snprintf(curpos, buflen - total_done, "%2d", lt->tm_mday);
+                               break;
+                       case 'F':
+                               len = 10; CHECK_SIZE();
+                               snprintf(curpos, buflen - total_done, "%4d-%02d-%02d", 
+                                       lt->tm_year + 1900, lt->tm_mon +1, lt->tm_mday);
+                               break;
+                       case 'H':
+                               total_done += 2; CHECK_SIZE();
+                               *curpos++ = '0'+(lt->tm_hour / 10);
+                               *curpos++ = '0'+(lt->tm_hour % 10);
+                               break;
+                       case 'I':
+                               total_done += 2; CHECK_SIZE();
+                               tmp = lt->tm_hour;
+                               if (tmp > 12)
+                                       tmp -= 12;
+                               else if (tmp == 0)
+                                       tmp = 12;
+                               *curpos++ = '0'+(tmp / 10);
+                               *curpos++ = '0'+(tmp % 10);
+                               break;
+                       case 'j':
+                               len = 3; CHECK_SIZE();
+                               snprintf(curpos, buflen - total_done, "%03d", lt->tm_yday+1);
+                               break;
+                       case 'k':
+                               len = 2; CHECK_SIZE();
+                               snprintf(curpos, buflen - total_done, "%2d", lt->tm_hour);
+                               break;
+                       case 'l':
+                               len = 2; CHECK_SIZE();
+                               tmp = lt->tm_hour;
+                               if (tmp > 12)
+                                       tmp -= 12;
+                               else if (tmp == 0)
+                                       tmp = 12;
+                               snprintf(curpos, buflen - total_done, "%2d", tmp);
+                               break;
+                       case 'm':
+                               total_done += 2; CHECK_SIZE();
+                               tmp = lt->tm_mon + 1;
+                               *curpos++ = '0'+(tmp / 10);
+                               *curpos++ = '0'+(tmp % 10);
+                               break;
+                       case 'M':
+                               total_done += 2; CHECK_SIZE();
+                               *curpos++ = '0'+(lt->tm_min / 10);
+                               *curpos++ = '0'+(lt->tm_min % 10);
+                               break;
+                       case 'n':
+                               len = 1; CHECK_SIZE();
+                               *curpos = '\n';
+                               break;
+                       case 'p':
+                               if (lt->tm_hour >= 12) {
+                                       len = strlen(s_pm_up); CHECK_SIZE();
+                                       snprintf(curpos, buflen-total_done, s_pm_up);
+                               } else {
+                                       len = strlen(s_am_up); CHECK_SIZE();
+                                       snprintf(curpos, buflen-total_done, s_am_up);
+                               }
+                               break;
+                       case 'P':
+                               if (lt->tm_hour >= 12) {
+                                       len = strlen(s_pm_low); CHECK_SIZE();
+                                       snprintf(curpos, buflen-total_done, s_pm_low);
+                               } else {
+                                       len = strlen(s_am_low); CHECK_SIZE();
+                                       snprintf(curpos, buflen-total_done, s_am_low);
+                               }
+                               break;
+                       case 'r':
+                               fast_strftime(subbuf, 64, time_am_pm, lt);
+                               len = strlen(subbuf); CHECK_SIZE();
+                               strncpy2(curpos, subbuf, buflen - total_done);
+                               break;
+                       case 'R':
+                               total_done += 5; CHECK_SIZE();
+                               *curpos++ = '0'+(lt->tm_hour / 10);
+                               *curpos++ = '0'+(lt->tm_hour % 10);
+                               *curpos++ = ':';
+                               *curpos++ = '0'+(lt->tm_min / 10);
+                               *curpos++ = '0'+(lt->tm_min % 10);
+                               break;
+                       case 's':
+                               snprintf(subbuf, buflen - total_done, "%ld", mktime(lt));
+                               len = strlen(subbuf); CHECK_SIZE();
+                               strncpy2(curpos, subbuf, buflen - total_done);
+                               break;
+                       case 'S':
+                               total_done += 2; CHECK_SIZE();
+                               *curpos++ = '0'+(lt->tm_sec / 10);
+                               *curpos++ = '0'+(lt->tm_sec % 10);
+                               break;
+                       case 't':
+                               len = 1; CHECK_SIZE();
+                               *curpos = '\t';
+                               break;
+                       case 'T':
+                               total_done += 8; CHECK_SIZE();
+                               *curpos++ = '0'+(lt->tm_hour / 10);
+                               *curpos++ = '0'+(lt->tm_hour % 10);
+                               *curpos++ = ':';
+                               *curpos++ = '0'+(lt->tm_min / 10);
+                               *curpos++ = '0'+(lt->tm_min % 10);
+                               *curpos++ = ':';
+                               *curpos++ = '0'+(lt->tm_sec / 10);
+                               *curpos++ = '0'+(lt->tm_sec % 10);
+                               break;
+                       case 'u':
+                               len = 1; CHECK_SIZE();
+                               snprintf(curpos, buflen - total_done, "%d", lt->tm_wday == 0 ? 7: lt->tm_wday);
+                               break;
+                       case 'w':
+                               len = 1; CHECK_SIZE();
+                               snprintf(curpos, buflen - total_done, "%d", lt->tm_wday);
+                               break;
+                       case 'x':
+                               fast_strftime(subbuf, 64, date_loc_format, lt);
+                               len = strlen(subbuf); CHECK_SIZE();
+                               strncpy2(curpos, subbuf, buflen - total_done);
+                               break;
+                       case 'X':
+                               fast_strftime(subbuf, 64, time_loc_format, lt);
+                               len = strlen(subbuf); CHECK_SIZE();
+                               strncpy2(curpos, subbuf, buflen - total_done);
+                               break;
+                       case 'y':
+                               total_done += 2; CHECK_SIZE();
+                               tmp = lt->tm_year%100;
+                               *curpos++ = '0'+(tmp / 10);
+                               *curpos++ = '0'+(tmp % 10);
+                               break;
+                       case 'Y':
+                               len = 4; CHECK_SIZE();
+                               snprintf(curpos, buflen - total_done, "%4d", lt->tm_year + 1900);
+                               break;
+                       case 'G':
+                       case 'g':
+                       case 'U':
+                       case 'V':
+                       case 'W':
+                       case 'z':
+                       case 'Z':
+                       case '+':
+                               /* let these complicated ones be done with the libc */
+                               snprintf(subfmt, 64, "%%%c", *format);
+                               strftime(subbuf, 64, subfmt, lt);
+                               len = strlen(subbuf); CHECK_SIZE();
+                               strncpy2(curpos, subbuf, buflen - total_done);
+                               break;
+                       case 'E':
+                       case 'O':
+                               /* let these complicated modifiers be done with the libc */
+                               snprintf(subfmt, 64, "%%%c%c", *format, *(format+1));
+                               strftime(subbuf, 64, subfmt, lt);
+                               len = strlen(subbuf); CHECK_SIZE();
+                               strncpy2(curpos, subbuf, buflen - total_done);
+                               format++;
+                               break;
+                       default:
+                               g_warning("format error (%c)", *format);
+                               *curpos = '\0';
+                               return total_done;
+                       }
+                       curpos += len;
+                       format++;
+               } else {
+                       int len = 1; CHECK_SIZE();
+                       *curpos++ = *format++; 
+               }
+       }
+       *curpos++ = '\0';
+       return total_done;
+}