7fe6c6557c32f4d2ce85b39821cbd5f9b013a1d8
[claws.git] / src / utils.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2001 Hiroyuki Yamamoto
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #endif
23
24 #include "defs.h"
25
26 #include <glib.h>
27 #include <stdio.h>
28 #include <string.h>
29 #include <ctype.h>
30 #include <errno.h>
31
32 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
33 #  include <wchar.h>
34 #  include <wctype.h>
35 #endif
36 #include <stdlib.h>
37 #include <sys/stat.h>
38 #include <unistd.h>
39 #include <stdarg.h>
40 #include <sys/types.h>
41 #include <sys/wait.h>
42 #include <dirent.h>
43 #include <time.h>
44
45 #include "intl.h"
46 #include "utils.h"
47 #include "statusbar.h"
48 #include "logwindow.h"
49
50 #define BUFFSIZE        8192
51
52 extern gboolean debug_mode;
53
54 static void hash_free_strings_func(gpointer key, gpointer value, gpointer data);
55
56 void list_free_strings(GList *list)
57 {
58         list = g_list_first(list);
59
60         while (list != NULL) {
61                 g_free(list->data);
62                 list = list->next;
63         }
64 }
65
66 void slist_free_strings(GSList *list)
67 {
68         while (list != NULL) {
69                 g_free(list->data);
70                 list = list->next;
71         }
72 }
73
74 static void hash_free_strings_func(gpointer key, gpointer value, gpointer data)
75 {
76         g_free(key);
77 }
78
79 void hash_free_strings(GHashTable *table)
80 {
81         g_hash_table_foreach(table, hash_free_strings_func, NULL);
82 }
83
84 void ptr_array_free_strings(GPtrArray *array)
85 {
86         gint i;
87         gchar *str;
88
89         g_return_if_fail(array != NULL);
90
91         for (i = 0; i < array->len; i++) {
92                 str = g_ptr_array_index(array, i);
93                 g_free(str);
94         }
95 }
96
97 gint to_number(const gchar *nstr)
98 {
99         register const gchar *p;
100
101         if (*nstr == '\0') return -1;
102
103         for (p = nstr; *p != '\0'; p++)
104                 if (!isdigit(*p)) return -1;
105
106         return atoi(nstr);
107 }
108
109 /* convert integer into string,
110    nstr must be not lower than 11 characters length */
111 gchar *itos_buf(gchar *nstr, gint n)
112 {
113         g_snprintf(nstr, 11, "%d", n);
114         return nstr;
115 }
116
117 /* convert integer into string */
118 gchar *itos(gint n)
119 {
120         static gchar nstr[11];
121
122         return itos_buf(nstr, n);
123 }
124
125 gchar *to_human_readable(off_t size)
126 {
127         static gchar str[9];
128         gint count;
129         guint32 div = 1;
130
131         for (count = 0; count < 3; count++) {
132                 if (size / div < 1024)
133                         break;
134                 else
135                         div *= 1024;
136         }
137
138         switch (count) {
139         case 0: g_snprintf(str, sizeof(str), "%dB",    (gint)size);   break;
140         case 1: g_snprintf(str, sizeof(str), "%.1fKB", (gfloat)size / div);
141                 break;
142         case 2: g_snprintf(str, sizeof(str), "%.1fMB", (gfloat)size / div);
143                 break;
144         default:
145                 g_snprintf(str, sizeof(str), "%.1fGB", (gfloat)size / div);
146                 break;
147         }
148
149         return str;
150 }
151
152 /* strcmp with NULL-checking */
153 gint strcmp2(const gchar *s1, const gchar *s2)
154 {
155         if (s1 == NULL || s2 == NULL)
156                 return -1;
157         else
158                 return strcmp(s1, s2);
159 }
160
161 /* compare paths */
162 gint path_cmp(const gchar *s1, const gchar *s2)
163 {
164         gint len1, len2;
165
166         if (s1 == NULL || s2 == NULL) return -1;
167         if (*s1 == '\0' || *s2 == '\0') return -1;
168
169         len1 = strlen(s1);
170         len2 = strlen(s2);
171
172         if (s1[len1 - 1] == G_DIR_SEPARATOR) len1--;
173         if (s2[len2 - 1] == G_DIR_SEPARATOR) len2--;
174
175         return strncmp(s1, s2, MAX(len1, len2));
176 }
177
178 /* remove trailing return code */
179 gchar *strretchomp(gchar *str)
180 {
181         register gchar *s;
182
183         if (!*str) return str;
184
185         for (s = str + strlen(str) - 1;
186              s >= str && (*s == '\n' || *s == '\r');
187              s--)
188                 *s = '\0';
189
190         return str;
191 }
192
193 /* remove trailing character */
194 gchar *strtailchomp(gchar *str, gchar tail_char)
195 {
196         register gchar *s;
197
198         if (!*str) return str;
199         if (tail_char == '\0') return str;
200
201         for (s = str + strlen(str) - 1; s >= str && *s == tail_char; s--)
202                 *s = '\0';
203
204         return str;
205 }
206
207
208 /* Similar to `strstr' but this function ignores the case of both strings.  */
209 gchar *strcasestr(const gchar *haystack, const gchar *needle)
210 {
211         register size_t haystack_len, needle_len;
212
213         haystack_len = strlen(haystack);
214         needle_len   = strlen(needle);
215
216         if (haystack_len < needle_len || needle_len == 0)
217                 return NULL;
218
219         while (haystack_len >= needle_len) {
220                 if (!strncasecmp(haystack, needle, needle_len))
221                         return (gchar *)haystack;
222                 else {
223                         haystack++;
224                         haystack_len--;
225                 }
226         }
227
228         return NULL;
229 }
230
231 /* Copy no more than N characters of SRC to DEST, with NULL terminating.  */
232 gchar *strncpy2(gchar *dest, const gchar *src, size_t n)
233 {
234         register gchar c;
235         gchar *s = dest;
236
237         do {
238                 if (--n == 0) {
239                         *dest = '\0';
240                         return s;
241                 }
242                 c = *src++;
243                 *dest++ = c;
244         } while (c != '\0');
245
246         /* don't do zero fill */
247         return s;
248 }
249
250 #if !HAVE_ISWALNUM
251 int iswalnum(wint_t wc)
252 {
253         return isalnum((int)wc);
254 }
255 #endif
256
257 #if !HAVE_ISWSPACE
258 int iswspace(wint_t wc)
259 {
260         return isspace((int)wc);
261 }
262 #endif
263
264 #if !HAVE_TOWLOWER
265 wint_t towlower(wint_t wc)
266 {
267         if (wc >= L'A' && wc <= L'Z')
268                 return wc + L'a' - L'A';
269
270         return wc;
271 }
272 #endif
273
274 #if !HAVE_WCSLEN
275 size_t wcslen(const wchar_t *s)
276 {
277         size_t len = 0;
278
279         while (*s != L'\0')
280                 ++len, ++s;
281
282         return len;
283 }
284 #endif
285
286 #if !HAVE_WCSCPY
287 /* Copy SRC to DEST.  */
288 wchar_t *wcscpy(wchar_t *dest, const wchar_t *src)
289 {
290         wint_t c;
291         wchar_t *s = dest;
292
293         do {
294                 c = *src++;
295                 *dest++ = c;
296         } while (c != L'\0');
297
298         return s;
299 }
300 #endif
301
302 #if !HAVE_WCSNCPY
303 /* Copy no more than N wide-characters of SRC to DEST.  */
304 wchar_t *wcsncpy (wchar_t *dest, const wchar_t *src, size_t n)
305 {
306         wint_t c;
307         wchar_t *s = dest;
308
309         do {
310                 c = *src++;
311                 *dest++ = c;
312                 if (--n == 0)
313                         return s;
314         } while (c != L'\0');
315
316         /* zero fill */
317         do
318                 *dest++ = L'\0';
319         while (--n > 0);
320
321         return s;
322 }
323 #endif
324
325 /* Duplicate S, returning an identical malloc'd string. */
326 wchar_t *wcsdup(const wchar_t *s)
327 {
328         wchar_t *new_str;
329
330         if (s) {
331                 new_str = g_new(wchar_t, wcslen(s) + 1);
332                 wcscpy(new_str, s);
333         } else
334                 new_str = NULL;
335
336         return new_str;
337 }
338
339 /* Duplicate no more than N wide-characters of S,
340    returning an identical malloc'd string. */
341 wchar_t *wcsndup(const wchar_t *s, size_t n)
342 {
343         wchar_t *new_str;
344
345         if (s) {
346                 new_str = g_new(wchar_t, n + 1);
347                 wcsncpy(new_str, s, n);
348                 new_str[n] = (wchar_t)0;
349         } else
350                 new_str = NULL;
351
352         return new_str;
353 }
354
355 wchar_t *strdup_mbstowcs(const gchar *s)
356 {
357         wchar_t *new_str;
358
359         if (s) {
360                 new_str = g_new(wchar_t, strlen(s) + 1);
361                 if (mbstowcs(new_str, s, strlen(s) + 1) < 0) {
362                         g_free(new_str);
363                         new_str = NULL;
364                 } else
365                         new_str = g_realloc(new_str,
366                                             sizeof(wchar_t) * (wcslen(new_str) + 1));
367         } else
368                 new_str = NULL;
369
370         return new_str;
371 }
372
373 gchar *strdup_wcstombs(const wchar_t *s)
374 {
375         gchar *new_str;
376         size_t len;
377
378         if (s) {
379                 len = wcslen(s) * MB_CUR_MAX + 1;
380                 new_str = g_new(gchar, len);
381                 if (wcstombs(new_str, s, len) < 0) {
382                         g_free(new_str);
383                         new_str = NULL;
384                 } else
385                         new_str = g_realloc(new_str, strlen(new_str) + 1);
386         } else
387                 new_str = NULL;
388
389         return new_str;
390 }
391
392 /* Compare S1 and S2, ignoring case.  */
393 gint wcsncasecmp(const wchar_t *s1, const wchar_t *s2, size_t n)
394 {
395         wint_t c1;
396         wint_t c2;
397
398         while (n--) {
399                 c1 = towlower(*s1++);
400                 c2 = towlower(*s2++);
401                 if (c1 != c2)
402                         return c1 - c2;
403                 else if (c1 == 0 && c2 == 0)
404                         break;
405         }
406
407         return 0;
408 }
409
410 /* Find the first occurrence of NEEDLE in HAYSTACK, ignoring case.  */
411 wchar_t *wcscasestr(const wchar_t *haystack, const wchar_t *needle)
412 {
413         register size_t haystack_len, needle_len;
414
415         haystack_len = wcslen(haystack);
416         needle_len   = wcslen(needle);
417
418         if (haystack_len < needle_len || needle_len == 0)
419                 return NULL;
420
421         while (haystack_len >= needle_len) {
422                 if (!wcsncasecmp(haystack, needle, needle_len))
423                         return (wchar_t *)haystack;
424                 else {
425                         haystack++;
426                         haystack_len--;
427                 }
428         }
429
430         return NULL;
431 }
432
433 /* Examine if next block is non-ASCII string */
434 gboolean is_next_nonascii(const wchar_t *s)
435 {
436         const wchar_t *wp;
437
438         /* skip head space */
439         for (wp = s; *wp != (wchar_t)0 && iswspace(*wp); wp++)
440                 ;
441         for (; *wp != (wchar_t)0 && !iswspace(*wp); wp++) {
442                 if (*wp > 127)
443                         return TRUE;
444         }
445
446         return FALSE;
447 }
448
449 /* Examine if next block is multi-byte string */
450 gboolean is_next_mbs(const wchar_t *s)
451 {
452         gint mbl;
453         const wchar_t *wp;
454         gchar tmp[MB_CUR_MAX];
455
456         /* skip head space */
457         for (wp = s; *wp != (wchar_t)0 && iswspace(*wp); wp++)
458                 ;
459         for (; *wp != (wchar_t)0 && !iswspace(*wp); wp++) {
460                 mbl = wctomb(tmp, *wp);
461                 if (mbl > 1)
462                         return TRUE;
463         }
464
465         return FALSE;
466 }
467
468 wchar_t *find_wspace(const wchar_t *s)
469 {
470         const wchar_t *wp;
471
472         for (wp = s; *wp != (wchar_t)0 && iswspace(*wp); wp++)
473                 ;
474         for (; *wp != (wchar_t)0; wp++) {
475                 if (iswspace(*wp))
476                         return (wchar_t *)wp;
477         }
478
479         return NULL;
480 }
481
482 /* compare subjects */
483 gint subject_compare(const gchar *s1, const gchar *s2)
484 {
485         gint retval;
486         gchar *str1, *str2;
487
488         if (!s1 || !s2) return -1;
489         if (!*s1 || !*s2) return -1;
490
491         Xalloca(str1, strlen(s1) + 1, return -1);
492         Xalloca(str2, strlen(s2) + 1, return -1);
493         strcpy(str1, s1);
494         strcpy(str2, s2);
495
496         trim_subject(str1);
497         trim_subject(str2);
498
499         if (!*str1 || !*str2) return -1;
500
501         retval = strcmp(str1, str2);
502         //if (retval == 0)
503         //      g_print("\ns1 = %s\ns2 = %s\n"
504         //              "str1 = %s\nstr2 = %s\nmatched.\n",
505         //              s1, s2, str1, str2);
506
507         return retval;
508 }
509
510 void trim_subject(gchar *str)
511 {
512         gchar *srcp;
513
514         eliminate_parenthesis(str, '[', ']');
515         eliminate_parenthesis(str, '(', ')');
516         g_strstrip(str);
517
518         while (!strncasecmp(str, "Re:", 3)) {
519                 srcp = str + 3;
520                 while (isspace(*srcp)) srcp++;
521                 memmove(str, srcp, strlen(srcp) + 1);
522         }
523 }
524
525 void eliminate_parenthesis(gchar *str, gchar op, gchar cl)
526 {
527         register gchar *srcp, *destp;
528         gint in_brace;
529
530         srcp = destp = str;
531
532         while ((destp = strchr(destp, op))) {
533                 in_brace = 1;
534                 srcp = destp + 1;
535                 while (*srcp) {
536                         if (*srcp == op)
537                                 in_brace++;
538                         else if (*srcp == cl)
539                                 in_brace--;
540                         srcp++;
541                         if (in_brace == 0)
542                                 break;
543                 }
544                 while (isspace(*srcp)) srcp++;
545                 memmove(destp, srcp, strlen(srcp) + 1);
546         }
547 }
548
549 void extract_parenthesis(gchar *str, gchar op, gchar cl)
550 {
551         register gchar *srcp, *destp;
552         gint in_brace;
553
554         srcp = destp = str;
555
556         while ((srcp = strchr(destp, op))) {
557                 if (destp > str)
558                         *destp++ = ' ';
559                 memmove(destp, srcp + 1, strlen(srcp));
560                 in_brace = 1;
561                 while(*destp) {
562                         if (*destp == op)
563                                 in_brace++;
564                         else if (*destp == cl)
565                                 in_brace--;
566
567                         if (in_brace == 0)
568                                 break;
569
570                         destp++;
571                 }
572         }
573         *destp = '\0';
574 }
575
576 void extract_one_parenthesis_with_skip_quote(gchar *str, gchar quote_chr,
577                                              gchar op, gchar cl)
578 {
579         register gchar *srcp, *destp;
580         gint in_brace;
581         gboolean in_quote = FALSE;
582
583         srcp = destp = str;
584
585         if ((srcp = strchr_with_skip_quote(destp, quote_chr, op))) {
586                 memmove(destp, srcp + 1, strlen(srcp));
587                 in_brace = 1;
588                 while(*destp) {
589                         if (*destp == op && !in_quote)
590                                 in_brace++;
591                         else if (*destp == cl && !in_quote)
592                                 in_brace--;
593                         else if (*destp == quote_chr)
594                                 in_quote ^= TRUE;
595
596                         if (in_brace == 0)
597                                 break;
598
599                         destp++;
600                 }
601         }
602         *destp = '\0';
603 }
604
605 void extract_parenthesis_with_skip_quote(gchar *str, gchar quote_chr,
606                                          gchar op, gchar cl)
607 {
608         register gchar *srcp, *destp;
609         gint in_brace;
610         gboolean in_quote = FALSE;
611
612         srcp = destp = str;
613
614         while ((srcp = strchr_with_skip_quote(destp, quote_chr, op))) {
615                 if (destp > str)
616                         *destp++ = ' ';
617                 memmove(destp, srcp + 1, strlen(srcp));
618                 in_brace = 1;
619                 while(*destp) {
620                         if (*destp == op && !in_quote)
621                                 in_brace++;
622                         else if (*destp == cl && !in_quote)
623                                 in_brace--;
624                         else if (*destp == quote_chr)
625                                 in_quote ^= TRUE;
626
627                         if (in_brace == 0)
628                                 break;
629
630                         destp++;
631                 }
632         }
633         *destp = '\0';
634 }
635
636 void eliminate_quote(gchar *str, gchar quote_chr)
637 {
638         register gchar *srcp, *destp;
639
640         srcp = destp = str;
641
642         while ((destp = strchr(destp, quote_chr))) {
643                 if ((srcp = strchr(destp + 1, quote_chr))) {
644                         srcp++;
645                         while (isspace(*srcp)) srcp++;
646                         memmove(destp, srcp, strlen(srcp) + 1);
647                 } else {
648                         *destp = '\0';
649                         break;
650                 }
651         }
652 }
653
654 void extract_quote(gchar *str, gchar quote_chr)
655 {
656         register gchar *p;
657
658         if ((str = strchr(str, quote_chr))) {
659                 if ((p = strchr(str + 1, quote_chr))) {
660                         *p = '\0';
661                         memmove(str, str + 1, p - str);
662                 }
663         }
664 }
665
666 void eliminate_address_comment(gchar *str)
667 {
668         register gchar *srcp, *destp;
669         gint in_brace;
670
671         srcp = destp = str;
672
673         while ((destp = strchr(destp, '"'))) {
674                 if ((srcp = strchr(destp + 1, '"'))) {
675                         srcp++;
676                         if (*srcp == '@') {
677                                 destp = srcp + 1;
678                         } else {
679                                 while (isspace(*srcp)) srcp++;
680                                 memmove(destp, srcp, strlen(srcp) + 1);
681                         }
682                 } else {
683                         *destp = '\0';
684                         break;
685                 }
686         }
687
688         srcp = destp = str;
689
690         while ((destp = strchr_with_skip_quote(destp, '"', '('))) {
691                 in_brace = 1;
692                 srcp = destp + 1;
693                 while (*srcp) {
694                         if (*srcp == '(')
695                                 in_brace++;
696                         else if (*srcp == ')')
697                                 in_brace--;
698                         srcp++;
699                         if (in_brace == 0)
700                                 break;
701                 }
702                 while (isspace(*srcp)) srcp++;
703                 memmove(destp, srcp, strlen(srcp) + 1);
704         }
705 }
706
707 gchar *strchr_with_skip_quote(const gchar *str, gint quote_chr, gint c)
708 {
709         gboolean in_quote = FALSE;
710
711         while (*str) {
712                 if (*str == c && !in_quote)
713                         return (gchar *)str;
714                 if (*str == quote_chr)
715                         in_quote ^= TRUE;
716                 str++;
717         }
718
719         return NULL;
720 }
721
722 gchar *strrchr_with_skip_quote(const gchar *str, gint quote_chr, gint c)
723 {
724         gboolean in_quote = FALSE;
725         const gchar *p;
726
727         p = str + strlen(str) - 1;
728         while (p >= str) {
729                 if (*p == c && !in_quote)
730                         return (gchar *)p;
731                 if (*p == quote_chr)
732                         in_quote ^= TRUE;
733                 p--;
734         }
735
736         return NULL;
737 }
738
739 void extract_address(gchar *str)
740 {
741         eliminate_address_comment(str);
742         if (strchr_with_skip_quote(str, '"', '<'))
743                 extract_parenthesis_with_skip_quote(str, '"', '<', '>');
744         g_strstrip(str);
745 }
746
747 GSList *address_list_append(GSList *addr_list, const gchar *str)
748 {
749         gchar *work;
750         gchar *workp;
751
752         if (!str) return addr_list;
753
754         Xstrdup_a(work, str, return addr_list);
755
756         eliminate_address_comment(work);
757         workp = work;
758
759         while (workp && *workp) {
760                 gchar *p, *next;
761
762                 if ((p = strchr_with_skip_quote(workp, '"', ','))) {
763                         *p = '\0';
764                         next = p + 1;
765                 } else
766                         next = NULL;
767
768                 if (strchr_with_skip_quote(workp, '"', '<'))
769                         extract_parenthesis_with_skip_quote
770                                 (workp, '"', '<', '>');
771
772                 g_strstrip(workp);
773                 if (*workp)
774                         addr_list = g_slist_append(addr_list, g_strdup(workp));
775
776                 workp = next;
777         }
778
779         return addr_list;
780 }
781
782 GSList *references_list_append(GSList *msgid_list, const gchar *str)
783 {
784         const gchar *strp;
785
786         if (!str) return msgid_list;
787         strp = str;
788
789         while (strp && *strp) {
790                 const gchar *start, *end;
791                 gchar *msgid;
792
793                 if ((start = strchr(strp, '<')) != NULL) {
794                         end = strchr(start + 1, '>');
795                         if (!end) break;
796                 } else
797                         break;
798
799                 msgid = g_strndup(start + 1, end - start - 1);
800                 g_strstrip(msgid);
801                 if (*msgid)
802                         msgid_list = g_slist_append(msgid_list, msgid);
803                 else
804                         g_free(msgid);
805
806                 strp = end + 1;
807         }
808
809         return msgid_list;
810 }
811
812 GSList *newsgroup_list_append(GSList *group_list, const gchar *str)
813 {
814         gchar *work;
815         gchar *workp;
816
817         if (!str) return group_list;
818
819         Xstrdup_a(work, str, return group_list);
820
821         workp = work;
822
823         while (workp && *workp) {
824                 gchar *p, *next;
825
826                 if ((p = strchr_with_skip_quote(workp, '"', ','))) {
827                         *p = '\0';
828                         next = p + 1;
829                 } else
830                         next = NULL;
831
832                 g_strstrip(workp);
833                 if (*workp)
834                         group_list = g_slist_append(group_list,
835                                                     g_strdup(workp));
836
837                 workp = next;
838         }
839
840         return group_list;
841 }
842
843 void remove_return(gchar *str)
844 {
845         register gchar *p = str;
846
847         while (*p) {
848                 if (*p == '\n' || *p == '\r')
849                         memmove(p, p + 1, strlen(p));
850                 else
851                         p++;
852         }
853 }
854
855 void remove_space(gchar *str)
856 {
857         register gchar *p = str;
858         register gint spc;
859
860         while (*p) {
861                 spc = 0;
862                 while (isspace(*(p + spc)))
863                         spc++;
864                 if (spc)
865                         memmove(p, p + spc, strlen(p + spc) + 1);
866                 else
867                         p++;
868         }
869 }
870
871 void unfold_line(gchar *str)
872 {
873         register gchar *p = str;
874         register gint spc;
875
876         while (*p) {
877                 if (*p == '\n' || *p == '\r') {
878                         *p++ = ' ';
879                         spc = 0;
880                         while (isspace(*(p + spc)))
881                                 spc++;
882                         if (spc)
883                                 memmove(p, p + spc, strlen(p + spc) + 1);
884                 } else
885                         p++;
886         }
887 }
888
889 void subst_char(gchar *str, gchar orig, gchar subst)
890 {
891         register gchar *p = str;
892
893         while (*p) {
894                 if (*p == orig)
895                         *p = subst;
896                 p++;
897         }
898 }
899
900 gboolean is_header_line(const gchar *str)
901 {
902         if (str[0] == ':') return FALSE;
903
904         while (*str != '\0' && *str != ' ') {
905                 if (*str == ':')
906                         return TRUE;
907                 str++;
908         }
909
910         return FALSE;
911 }
912
913 gboolean is_ascii_str(const guchar *str)
914 {
915         while (*str != '\0') {
916                 if (*str != '\t' && *str != ' ' &&
917                     *str != '\r' && *str != '\n' &&
918                     (*str < 32 || *str >= 127))
919                         return FALSE;
920                 str++;
921         }
922
923         return TRUE;
924 }
925
926 gint get_quote_level(const gchar *str)
927 {
928         size_t firstquotepos;
929         size_t lastquotepos = -1;
930         const gchar *p = str;
931         const gchar *pos;
932         gint quotelevel = -1;
933         gint i = 0;
934
935         /* speed up line processing by only searching to the last '>' */
936         if ((pos = strchr(str, '>')) != NULL) {
937                 firstquotepos = pos - str;
938                 lastquotepos = strrchr(str, '>') - str + 1;
939
940                 /* skip a line if it contains a '<' before the initial '>' */
941                 if (memchr(str, '<', pos - str) != NULL)
942                         return -1;
943         } else
944                 return -1;
945
946         while (i < lastquotepos) {
947                 while (i < lastquotepos) {
948                         if (isspace(*p) || (*p == '\t')) {
949                                 p++;
950                                 i++;
951                         } else
952                                 break;
953                 }
954                 if (i >= lastquotepos)
955                         break;
956
957                 if (*p == '>')
958                         quotelevel++;
959                 else if ((*p != '-') && !isspace(*p) && (i < lastquotepos)) {
960                         /* any characters are allowed except '-' and space */
961                         while ((*p != '-') && (*p != '>') && !isspace(*p) &&
962                                (i < lastquotepos)) {
963                                 p++;
964                                 i++;
965                         }
966                         if (*p == '>')
967                                 quotelevel++;
968                         else if ((i >= lastquotepos) || isspace(*p))
969                                 break;
970                 }
971
972                 p++;
973                 i++;
974         }
975
976         return quotelevel;
977 }
978
979 GList *uri_list_extract_filenames(const gchar *uri_list)
980 {
981         GList *result = NULL;
982         const gchar *p, *q;
983         gchar *file;
984
985         p = uri_list;
986
987         while (p) {
988                 if (*p != '#') {
989                         while (isspace(*p)) p++;
990                         if (!strncmp(p, "file:", 5)) {
991                                 p += 5;
992                                 q = p;
993                                 while (*q && *q != '\n' && *q != '\r') q++;
994
995                                 if (q > p) {
996                                         q--;
997                                         while (q > p && isspace(*q)) q--;
998                                         file = g_malloc(q - p + 2);
999                                         strncpy(file, p, q - p + 1);
1000                                         file[q - p + 1] = '\0';
1001                                         result = g_list_append(result,file);
1002                                 }
1003                         }
1004                 }
1005                 p = strchr(p, '\n');
1006                 if (p) p++;
1007         }
1008
1009         return result;
1010 }
1011
1012 gchar *strstr_with_skip_quote(const gchar *haystack, const gchar *needle)
1013 {
1014         register guint haystack_len, needle_len;
1015         gboolean in_squote = FALSE, in_dquote = FALSE;
1016
1017         haystack_len = strlen(haystack);
1018         needle_len   = strlen(needle);
1019
1020         if (haystack_len < needle_len || needle_len == 0)
1021                 return NULL;
1022
1023         while (haystack_len >= needle_len) {
1024                 if (!in_squote && !in_dquote &&
1025                     !strncmp(haystack, needle, needle_len))
1026                         return (gchar *)haystack;
1027
1028                 /* 'foo"bar"' -> foo"bar"
1029                    "foo'bar'" -> foo'bar' */
1030                 if (*haystack == '\'') {
1031                         if (in_squote)
1032                                 in_squote = FALSE;
1033                         else if (!in_dquote)
1034                                 in_squote = TRUE;
1035                 } else if (*haystack == '\"') {
1036                         if (in_dquote)
1037                                 in_dquote = FALSE;
1038                         else if (!in_squote)
1039                                 in_dquote = TRUE;
1040                 }
1041
1042                 haystack++;
1043                 haystack_len--;
1044         }
1045
1046         return NULL;
1047 }
1048
1049 /* this fuction was taken from gstrfuncs.c in glib. */
1050 gchar **strsplit_with_quote(const gchar *str, const gchar *delim,
1051                             gint max_tokens)
1052 {
1053         GSList *string_list = NULL, *slist;
1054         gchar **str_array, *s;
1055         guint i, n = 1;
1056
1057         g_return_val_if_fail(str != NULL, NULL);
1058         g_return_val_if_fail(delim != NULL, NULL);
1059
1060         if (max_tokens < 1)
1061                 max_tokens = G_MAXINT;
1062
1063         s = strstr_with_skip_quote(str, delim);
1064         if (s) {
1065                 guint delimiter_len = strlen(delim);
1066
1067                 do {
1068                         guint len;
1069                         gchar *new_str;
1070
1071                         len = s - str;
1072                         new_str = g_new(gchar, len + 1);
1073                         strncpy(new_str, str, len);
1074                         new_str[len] = 0;
1075                         string_list = g_slist_prepend(string_list, new_str);
1076                         n++;
1077                         str = s + delimiter_len;
1078                         s = strstr_with_skip_quote(str, delim);
1079                 } while (--max_tokens && s);
1080         }
1081
1082         if (*str) {
1083                 n++;
1084                 string_list = g_slist_prepend(string_list, g_strdup(str));
1085         }
1086
1087         str_array = g_new(gchar*, n);
1088
1089         i = n - 1;
1090
1091         str_array[i--] = NULL;
1092         for (slist = string_list; slist; slist = slist->next)
1093                 str_array[i--] = slist->data;
1094
1095         g_slist_free(string_list);
1096
1097         return str_array;
1098 }
1099
1100 /*
1101  * We need this wrapper around g_get_home_dir(), so that
1102  * we can fix some Windoze things here.  Should be done in glibc of course
1103  * but as long as we are not able to do our own extensions to glibc, we do 
1104  * it here.
1105  */
1106 gchar *get_home_dir(void)
1107 {
1108 #if HAVE_DOSISH_SYSTEM
1109     static gchar *home_dir;
1110
1111     if (!home_dir) {
1112         home_dir = read_w32_registry_string(NULL,
1113                                             "Software\\Sylpheed", "HomeDir" );
1114         if (!home_dir || !*home_dir) {
1115             if (getenv ("HOMEDRIVE") && getenv("HOMEPATH")) {
1116                 const char *s = g_get_home_dir();
1117                 if (s && *s)
1118                     home_dir = g_strdup (s);
1119             }
1120             if (!home_dir || !*home_dir) 
1121                 home_dir = g_strdup ("c:\\sylpheed");
1122         }
1123         debug_print("initialized home_dir to `%s'\n", home_dir);
1124     }
1125     return home_dir;
1126 #else /* standard glib */
1127     return g_get_home_dir();
1128 #endif
1129 }
1130
1131 gchar *get_rc_dir(void)
1132 {
1133         static gchar *rc_dir = NULL;
1134
1135         if (!rc_dir)
1136                 rc_dir = g_strconcat(get_home_dir(), G_DIR_SEPARATOR_S,
1137                                      RC_DIR, NULL);
1138
1139         return rc_dir;
1140 }
1141
1142 gchar *get_news_cache_dir(void)
1143 {
1144         static gchar *news_cache_dir = NULL;
1145
1146         if (!news_cache_dir)
1147                 news_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1148                                              NEWS_CACHE_DIR, NULL);
1149
1150         return news_cache_dir;
1151 }
1152
1153 gchar *get_imap_cache_dir(void)
1154 {
1155         static gchar *imap_cache_dir = NULL;
1156
1157         if (!imap_cache_dir)
1158                 imap_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1159                                              IMAP_CACHE_DIR, NULL);
1160
1161         return imap_cache_dir;
1162 }
1163
1164 gchar *get_mbox_cache_dir(void)
1165 {
1166         static gchar *mbox_cache_dir = NULL;
1167
1168         if (!mbox_cache_dir)
1169                 mbox_cache_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1170                                              MBOX_CACHE_DIR, NULL);
1171
1172         return mbox_cache_dir;
1173 }
1174
1175 gchar *get_mime_tmp_dir(void)
1176 {
1177         static gchar *mime_tmp_dir = NULL;
1178
1179         if (!mime_tmp_dir)
1180                 mime_tmp_dir = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1181                                            MIME_TMP_DIR, NULL);
1182
1183         return mime_tmp_dir;
1184 }
1185
1186 gchar *get_tmp_file(void)
1187 {
1188         static gchar *tmp_file = NULL;
1189
1190         if (!tmp_file)
1191                 tmp_file = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
1192                                        "tmpfile", NULL);
1193
1194         return tmp_file;
1195 }
1196
1197 gchar *get_domain_name(void)
1198 {
1199         static gchar *domain_name = NULL;
1200
1201         if (!domain_name) {
1202                 gchar buf[BUFFSIZE] = "";
1203
1204                 if (gethostname(buf, sizeof(buf)) < 0) {
1205                         perror("gethostname");
1206                         strcpy(buf, "unknown");
1207                 }
1208
1209                 domain_name = g_strdup(buf);
1210         }
1211
1212         return domain_name;
1213 }
1214
1215 off_t get_file_size(const gchar *file)
1216 {
1217         struct stat s;
1218
1219         if (stat(file, &s) < 0) {
1220                 FILE_OP_ERROR(file, "stat");
1221                 return -1;
1222         }
1223
1224         return s.st_size;
1225 }
1226
1227 off_t get_left_file_size(FILE *fp)
1228 {
1229         glong pos;
1230         glong end;
1231         off_t size;
1232
1233         if ((pos = ftell(fp)) < 0) {
1234                 perror("ftell");
1235                 return -1;
1236         }
1237         if (fseek(fp, 0L, SEEK_END) < 0) {
1238                 perror("fseek");
1239                 return -1;
1240         }
1241         if ((end = ftell(fp)) < 0) {
1242                 perror("fseek");
1243                 return -1;
1244         }
1245         size = end - pos;
1246         if (fseek(fp, pos, SEEK_SET) < 0) {
1247                 perror("fseek");
1248                 return -1;
1249         }
1250
1251         return size;
1252 }
1253
1254 gboolean file_exist(const gchar *file, gboolean allow_fifo)
1255 {
1256         struct stat s;
1257
1258         if (stat(file, &s) < 0) {
1259                 if (ENOENT != errno) FILE_OP_ERROR(file, "stat");
1260                 return FALSE;
1261         }
1262
1263         if (S_ISREG(s.st_mode) || (allow_fifo && S_ISFIFO(s.st_mode)))
1264                 return TRUE;
1265
1266         return FALSE;
1267 }
1268
1269 gboolean is_dir_exist(const gchar *dir)
1270 {
1271         struct stat s;
1272
1273         if (stat(dir, &s) < 0) {
1274                 if (ENOENT != errno) FILE_OP_ERROR(dir, "stat");
1275                 return FALSE;
1276         }
1277
1278         if (S_ISDIR(s.st_mode))
1279                 return TRUE;
1280
1281         return FALSE;
1282 }
1283
1284 gint change_dir(const gchar *dir)
1285 {
1286         gchar *prevdir = NULL;
1287
1288         if (debug_mode)
1289                 prevdir = g_get_current_dir();
1290
1291         if (chdir(dir) < 0) {
1292                 FILE_OP_ERROR(dir, "chdir");
1293                 if (debug_mode) g_free(prevdir);
1294                 return -1;
1295         } else if (debug_mode) {
1296                 gchar *cwd;
1297
1298                 cwd = g_get_current_dir();
1299                 if (strcmp(prevdir, cwd) != 0)
1300                         g_print("current dir: %s\n", cwd);
1301                 g_free(cwd);
1302                 g_free(prevdir);
1303         }
1304
1305         return 0;
1306 }
1307
1308 gint make_dir_hier(const gchar *dir)
1309 {
1310         gchar *parent_dir;
1311         const gchar *p;
1312
1313         for (p = dir; (p = strchr(p, G_DIR_SEPARATOR)) != NULL; p++) {
1314                 parent_dir = g_strndup(dir, p - dir);
1315                 if (*parent_dir != '\0') {
1316                         if (!is_dir_exist(parent_dir)) {
1317                                 if (mkdir(parent_dir, S_IRWXU) < 0) {
1318                                         FILE_OP_ERROR(parent_dir, "mkdir");
1319                                         g_free(parent_dir);
1320                                         return -1;
1321                                 }
1322                                 if (chmod(parent_dir, S_IRWXU) < 0)
1323                                         FILE_OP_ERROR(parent_dir, "chmod");
1324                         }
1325                 }
1326                 g_free(parent_dir);
1327         }
1328         if (!is_dir_exist(dir)) {
1329                 if (mkdir(dir, S_IRWXU) < 0) {
1330                         FILE_OP_ERROR(dir, "mkdir");
1331                         return -1;
1332                 }
1333                 if (chmod(dir, S_IRWXU) < 0)
1334                         FILE_OP_ERROR(dir, "chmod");
1335         }
1336
1337         return 0;
1338 }
1339
1340 gint remove_all_files(const gchar *dir)
1341 {
1342         DIR *dp;
1343         struct dirent *d;
1344         gchar *prev_dir;
1345
1346         prev_dir = g_get_current_dir();
1347
1348         if (chdir(dir) < 0) {
1349                 FILE_OP_ERROR(dir, "chdir");
1350                 return -1;
1351         }
1352
1353         if ((dp = opendir(".")) == NULL) {
1354                 FILE_OP_ERROR(dir, "opendir");
1355                 return -1;
1356         }
1357
1358         while ((d = readdir(dp)) != NULL) {
1359                 if (!strcmp(d->d_name, ".") ||
1360                     !strcmp(d->d_name, ".."))
1361                         continue;
1362
1363                 if (unlink(d->d_name) < 0)
1364                         FILE_OP_ERROR(d->d_name, "unlink");
1365         }
1366
1367         closedir(dp);
1368
1369         if (chdir(prev_dir) < 0) {
1370                 FILE_OP_ERROR(prev_dir, "chdir");
1371                 g_free(prev_dir);
1372                 return -1;
1373         }
1374
1375         g_free(prev_dir);
1376
1377         return 0;
1378 }
1379
1380 gint remove_all_numbered_files(const gchar *dir)
1381 {
1382         DIR *dp;
1383         struct dirent *d;
1384         gchar *prev_dir;
1385
1386         prev_dir = g_get_current_dir();
1387
1388         if (chdir(dir) < 0) {
1389                 FILE_OP_ERROR(dir, "chdir");
1390                 return -1;
1391         }
1392
1393         if ((dp = opendir(".")) == NULL) {
1394                 FILE_OP_ERROR(dir, "opendir");
1395                 return -1;
1396         }
1397
1398         while ((d = readdir(dp)) != NULL) {
1399                 if (to_number(d->d_name) < 0) continue;
1400
1401                 if (unlink(d->d_name) < 0)
1402                         FILE_OP_ERROR(d->d_name, "unlink");
1403         }
1404
1405         closedir(dp);
1406
1407         if (chdir(prev_dir) < 0) {
1408                 FILE_OP_ERROR(prev_dir, "chdir");
1409                 g_free(prev_dir);
1410                 return -1;
1411         }
1412
1413         g_free(prev_dir);
1414
1415         return 0;
1416 }
1417
1418 gint remove_dir_recursive(const gchar *dir)
1419 {
1420         struct stat s;
1421         DIR *dp;
1422         struct dirent *d;
1423         gchar *prev_dir;
1424
1425         //g_print("dir = %s\n", dir);
1426
1427         if (stat(dir, &s) < 0) {
1428                 FILE_OP_ERROR(dir, "stat");
1429                 if (ENOENT == errno) return 0;
1430                 return -1;
1431         }
1432
1433         if (!S_ISDIR(s.st_mode)) {
1434                 if (unlink(dir) < 0) {
1435                         FILE_OP_ERROR(dir, "unlink");
1436                         return -1;
1437                 }
1438
1439                 return 0;
1440         }
1441
1442         prev_dir = g_get_current_dir();
1443         //g_print("prev_dir = %s\n", prev_dir);
1444
1445         if (!path_cmp(prev_dir, dir)) {
1446                 g_free(prev_dir);
1447                 if (chdir("..") < 0) {
1448                         FILE_OP_ERROR(dir, "chdir");
1449                         return -1;
1450                 }
1451                 prev_dir = g_get_current_dir();
1452         }
1453
1454         if (chdir(dir) < 0) {
1455                 FILE_OP_ERROR(dir, "chdir");
1456                 g_free(prev_dir);
1457                 return -1;
1458         }
1459
1460         if ((dp = opendir(".")) == NULL) {
1461                 FILE_OP_ERROR(dir, "opendir");
1462                 chdir(prev_dir);
1463                 g_free(prev_dir);
1464                 return -1;
1465         }
1466
1467         /* remove all files in the directory */
1468         while ((d = readdir(dp)) != NULL) {
1469                 if (!strcmp(d->d_name, ".") ||
1470                     !strcmp(d->d_name, ".."))
1471                         continue;
1472
1473                 if (stat(d->d_name, &s) < 0) {
1474                         FILE_OP_ERROR(d->d_name, "stat");
1475                         continue;
1476                 }
1477
1478                 //g_print("removing %s\n", d->d_name);
1479
1480                 if (S_ISDIR(s.st_mode)) {
1481                         if (remove_dir_recursive(d->d_name) < 0) {
1482                                 g_warning("can't remove directory\n");
1483                                 return -1;
1484                         }
1485                 } else {
1486                         if (unlink(d->d_name) < 0)
1487                                 FILE_OP_ERROR(d->d_name, "unlink");
1488                 }
1489         }
1490
1491         closedir(dp);
1492
1493         if (chdir(prev_dir) < 0) {
1494                 FILE_OP_ERROR(prev_dir, "chdir");
1495                 g_free(prev_dir);
1496                 return -1;
1497         }
1498
1499         g_free(prev_dir);
1500
1501         if (rmdir(dir) < 0) {
1502                 FILE_OP_ERROR(dir, "rmdir");
1503                 return -1;
1504         }
1505
1506         return 0;
1507 }
1508
1509 #if 0
1510 /* this seems to be slower than the stdio version... */
1511 gint copy_file(const gchar *src, const gchar *dest)
1512 {
1513         gint src_fd, dest_fd;
1514         gint n_read;
1515         gint n_write;
1516         gchar buf[BUFSIZ];
1517         gchar *dest_bak = NULL;
1518
1519         if ((src_fd = open(src, O_RDONLY)) < 0) {
1520                 FILE_OP_ERROR(src, "open");
1521                 return -1;
1522         }
1523
1524         if (is_file_exist(dest)) {
1525                 dest_bak = g_strconcat(dest, ".bak", NULL);
1526                 if (rename(dest, dest_bak) < 0) {
1527                         FILE_OP_ERROR(dest, "rename");
1528                         close(src_fd);
1529                         g_free(dest_bak);
1530                         return -1;
1531                 }
1532         }
1533
1534         if ((dest_fd = open(dest, O_RDWR|O_CREAT, S_IRUSR|S_IWUSR)) < 0) {
1535                 FILE_OP_ERROR(dest, "open");
1536                 close(src_fd);
1537                 if (dest_bak) {
1538                         if (rename(dest_bak, dest) < 0)
1539                                 FILE_OP_ERROR(dest_bak, "rename");
1540                         g_free(dest_bak);
1541                 }
1542                 return -1;
1543         }
1544
1545         while ((n_read = read(src_fd, buf, sizeof(buf))) > 0) {
1546                 gint len = n_read;
1547                 gchar *bufp = buf;
1548
1549                 while (len > 0) {
1550                         n_write = write(dest_fd, bufp, len);
1551                         if (n_write <= 0) {
1552                                 g_warning(_("writing to %s failed.\n"), dest);
1553                                 close(dest_fd);
1554                                 close(src_fd);
1555                                 unlink(dest);
1556                                 if (dest_bak) {
1557                                         if (rename(dest_bak, dest) < 0)
1558                                                 FILE_OP_ERROR(dest_bak, "rename");
1559                                         g_free(dest_bak);
1560                                 }
1561                                 return -1;
1562                         }
1563                         len -= n_write;
1564                         bufp += n_write;
1565                 }
1566         }
1567
1568         close(src_fd);
1569         close(dest_fd);
1570
1571         if (n_read < 0 || get_file_size(src) != get_file_size(dest)) {
1572                 g_warning(_("File copy from %s to %s failed.\n"), src, dest);
1573                 unlink(dest);
1574                 if (dest_bak) {
1575                         if (rename(dest_bak, dest) < 0)
1576                                 FILE_OP_ERROR(dest_bak, "rename");
1577                         g_free(dest_bak);
1578                 }
1579                 return -1;
1580         }
1581         g_free(dest_bak);
1582
1583         return 0;
1584 }
1585 #endif
1586
1587 gint copy_file(const gchar *src, const gchar *dest)
1588 {
1589         FILE *src_fp, *dest_fp;
1590         gint n_read;
1591         gchar buf[BUFSIZ];
1592         gchar *dest_bak = NULL;
1593         gboolean err = FALSE;
1594
1595         if ((src_fp = fopen(src, "r")) == NULL) {
1596                 FILE_OP_ERROR(src, "fopen");
1597                 return -1;
1598         }
1599         if (is_file_exist(dest)) {
1600                 dest_bak = g_strconcat(dest, ".bak", NULL);
1601                 if (rename(dest, dest_bak) < 0) {
1602                         FILE_OP_ERROR(dest, "rename");
1603                         fclose(src_fp);
1604                         g_free(dest_bak);
1605                         return -1;
1606                 }
1607         }
1608
1609         if ((dest_fp = fopen(dest, "w")) == NULL) {
1610                 FILE_OP_ERROR(dest, "fopen");
1611                 fclose(src_fp);
1612                 if (dest_bak) {
1613                         if (rename(dest_bak, dest) < 0)
1614                                 FILE_OP_ERROR(dest_bak, "rename");
1615                         g_free(dest_bak);
1616                 }
1617                 return -1;
1618         }
1619
1620         if (change_file_mode_rw(dest_fp, dest) < 0) {
1621                 FILE_OP_ERROR(dest, "chmod");
1622                 g_warning(_("can't change file mode\n"));
1623         }
1624
1625         while ((n_read = fread(buf, sizeof(gchar), sizeof(buf), src_fp)) > 0) {
1626                 if (n_read < sizeof(buf) && ferror(src_fp))
1627                         break;
1628                 if (fwrite(buf, n_read, 1, dest_fp) < 1) {
1629                         g_warning(_("writing to %s failed.\n"), dest);
1630                         fclose(dest_fp);
1631                         fclose(src_fp);
1632                         unlink(dest);
1633                         if (dest_bak) {
1634                                 if (rename(dest_bak, dest) < 0)
1635                                         FILE_OP_ERROR(dest_bak, "rename");
1636                                 g_free(dest_bak);
1637                         }
1638                         return -1;
1639                 }
1640         }
1641
1642         if (ferror(src_fp)) {
1643                 FILE_OP_ERROR(src, "fread");
1644                 err = TRUE;
1645         }
1646         fclose(src_fp);
1647         if (fclose(dest_fp) == EOF) {
1648                 FILE_OP_ERROR(dest, "fclose");
1649                 err = TRUE;
1650         }
1651
1652         if (err) {
1653                 unlink(dest);
1654                 if (dest_bak) {
1655                         if (rename(dest_bak, dest) < 0)
1656                                 FILE_OP_ERROR(dest_bak, "rename");
1657                         g_free(dest_bak);
1658                 }
1659                 return -1;
1660         }
1661
1662         g_free(dest_bak);
1663
1664         return 0;
1665 }
1666
1667 gint move_file(const gchar *src, const gchar *dest)
1668 {
1669         if (is_file_exist(dest)) {
1670                 g_warning(_("move_file(): file %s already exists."), dest);
1671                 return -1;
1672         }
1673
1674         if (rename(src, dest) == 0) return 0;
1675
1676         if (EXDEV != errno) {
1677                 FILE_OP_ERROR(src, "rename");
1678                 return -1;
1679         }
1680
1681         if (copy_file(src, dest) < 0) return -1;
1682
1683         unlink(src);
1684
1685         return 0;
1686 }
1687
1688 gint change_file_mode_rw(FILE *fp, const gchar *file)
1689 {
1690 #if HAVE_FCHMOD
1691         return fchmod(fileno(fp), S_IRUSR|S_IWUSR);
1692 #else
1693         return chmod(file, S_IRUSR|S_IWUSR);
1694 #endif
1695 }
1696
1697 FILE *my_tmpfile(void)
1698 {
1699 #if HAVE_MKSTEMP
1700         const gchar suffix[] = ".XXXXXX";
1701         const gchar *tmpdir;
1702         guint tmplen;
1703         const gchar *progname;
1704         guint proglen;
1705         gchar *fname;
1706         gint fd;
1707         FILE *fp;
1708
1709         tmpdir = g_get_tmp_dir();
1710         tmplen = strlen(tmpdir);
1711         progname = g_get_prgname();
1712         proglen = strlen(progname);
1713         Xalloca(fname, tmplen + 1 + proglen + sizeof(suffix),
1714                 return tmpfile());
1715
1716         memcpy(fname, tmpdir, tmplen);
1717         fname[tmplen] = G_DIR_SEPARATOR;
1718         memcpy(fname + tmplen + 1, progname, proglen);
1719         memcpy(fname + tmplen + 1 + proglen, suffix, sizeof(suffix));
1720
1721         fd = mkstemp(fname);
1722         if (fd < 0)
1723                 return tmpfile();
1724
1725         unlink(fname);
1726
1727         fp = fdopen(fd, "w+b");
1728         if (!fp)
1729                 close(fd);
1730         else
1731                 return fp;
1732 #endif /* HAVE_MKSTEMP */
1733
1734         return tmpfile();
1735 }
1736
1737 gint execute_async(gchar *const argv[])
1738 {
1739         pid_t pid;
1740
1741         if ((pid = fork()) < 0) {
1742                 perror("fork");
1743                 return -1;
1744         }
1745
1746         if (pid == 0) {                 /* child process */
1747                 pid_t gch_pid;
1748
1749                 if ((gch_pid = fork()) < 0) {
1750                         perror("fork");
1751                         _exit(1);
1752                 }
1753
1754                 if (gch_pid == 0) {     /* grandchild process */
1755                         execvp(argv[0], argv);
1756
1757                         perror("execvp");
1758                         _exit(1);
1759                 }
1760
1761                 _exit(0);
1762         }
1763
1764         waitpid(pid, NULL, 0);
1765
1766         return 0;
1767 }
1768
1769 gint execute_command_line(const gchar *cmdline)
1770 {
1771         gchar **argv;
1772         gint i;
1773         gint ret;
1774
1775         argv = strsplit_with_quote(cmdline, " ", 0);
1776
1777         for (i = 0; argv[i] != NULL; i++) {
1778                 gchar *str = argv[i];
1779
1780                 if (str[0] == '\'' || str[0] == '\"') {
1781                         gint len;
1782
1783                         len = strlen(str);
1784                         if (str[len - 1] == str[0]) {
1785                                 str[len - 1] = '\0';
1786                                 memmove(str, str + 1, len - 1);
1787                         }
1788                 }
1789         }
1790
1791         ret = execute_async(argv);
1792         g_strfreev(argv);
1793
1794         return ret;
1795 }
1796
1797 gint open_uri(const gchar *uri, const gchar *cmdline)
1798 {
1799         static gchar *default_cmdline = "netscape -remote openURL(%s,raise)";
1800         gchar buf[BUFFSIZE];
1801         gchar *p;
1802
1803         g_return_val_if_fail(uri != NULL, -1);
1804
1805         if (cmdline &&
1806             (p = strchr(cmdline, '%')) && *(p + 1) == 's' &&
1807             !strchr(p + 2, '%'))
1808                 g_snprintf(buf, sizeof(buf), cmdline, uri);
1809         else {
1810                 if (cmdline)
1811                         g_warning(_("Open URI command line is invalid: `%s'"),
1812                                   cmdline);
1813                 g_snprintf(buf, sizeof(buf), default_cmdline, uri);
1814         }
1815
1816         execute_command_line(buf);
1817
1818         return 0;
1819 }
1820
1821 time_t remote_tzoffset_sec(const gchar *zone)
1822 {
1823         static gchar ustzstr[] = "PSTPDTMSTMDTCSTCDTESTEDT";
1824         gchar zone3[4];
1825         gchar *p;
1826         gchar c;
1827         gint iustz;
1828         gint h, m;
1829         time_t remoteoffset;
1830
1831         strncpy(zone3, zone, 3);
1832         zone3[3] = '\0';
1833         remoteoffset = 0;
1834
1835         if (sscanf(zone, "%c%2d%2d", &c, &h, &m) == 3 &&
1836             (c == '+' || c == '-')) {
1837                 remoteoffset = ((h * 60) + m) * 60;
1838                 if (c == '-')
1839                         remoteoffset = -remoteoffset;
1840         } else if (!strncmp(zone, "UT" , 2) ||
1841                    !strncmp(zone, "GMT", 2)) {
1842                 remoteoffset = 0;
1843         } else if (strlen(zone3) == 3 &&
1844                    (p = strstr(ustzstr, zone3)) != NULL &&
1845                    (p - ustzstr) % 3 == 0) {
1846                 iustz = ((gint)(p - ustzstr) / 3 + 1) / 2 - 8;
1847                 remoteoffset = iustz * 3600;
1848         } else if (strlen(zone3) == 1) {
1849                 switch (zone[0]) {
1850                 case 'Z': remoteoffset =   0; break;
1851                 case 'A': remoteoffset =  -1; break;
1852                 case 'B': remoteoffset =  -2; break;
1853                 case 'C': remoteoffset =  -3; break;
1854                 case 'D': remoteoffset =  -4; break;
1855                 case 'E': remoteoffset =  -5; break;
1856                 case 'F': remoteoffset =  -6; break;
1857                 case 'G': remoteoffset =  -7; break;
1858                 case 'H': remoteoffset =  -8; break;
1859                 case 'I': remoteoffset =  -9; break;
1860                 case 'K': remoteoffset = -10; break; /* J is not used */
1861                 case 'L': remoteoffset = -11; break;
1862                 case 'M': remoteoffset = -12; break;
1863                 case 'N': remoteoffset =   1; break;
1864                 case 'O': remoteoffset =   2; break;
1865                 case 'P': remoteoffset =   3; break;
1866                 case 'Q': remoteoffset =   4; break;
1867                 case 'R': remoteoffset =   5; break;
1868                 case 'S': remoteoffset =   6; break;
1869                 case 'T': remoteoffset =   7; break;
1870                 case 'U': remoteoffset =   8; break;
1871                 case 'V': remoteoffset =   9; break;
1872                 case 'W': remoteoffset =  10; break;
1873                 case 'X': remoteoffset =  11; break;
1874                 case 'Y': remoteoffset =  12; break;
1875                 default:  remoteoffset =   0; break;
1876                 }
1877                 remoteoffset = remoteoffset * 3600;
1878         }
1879
1880         return remoteoffset;
1881 }
1882
1883 time_t tzoffset_sec(time_t *now)
1884 {
1885         struct tm gmt, *lt;
1886         gint off;
1887
1888         gmt = *gmtime(now);
1889         lt = localtime(now);
1890
1891         off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
1892
1893         if (lt->tm_year < gmt.tm_year)
1894                 off -= 24 * 60;
1895         else if (lt->tm_year > gmt.tm_year)
1896                 off += 24 * 60;
1897         else if (lt->tm_yday < gmt.tm_yday)
1898                 off -= 24 * 60;
1899         else if (lt->tm_yday > gmt.tm_yday)
1900                 off += 24 * 60;
1901
1902         if (off >= 24 * 60)             /* should be impossible */
1903                 off = 23 * 60 + 59;     /* if not, insert silly value */
1904         if (off <= -24 * 60)
1905                 off = -(23 * 60 + 59);
1906         if (off > 12 * 60)
1907                 off -= 24 * 60;
1908         if (off < -12 * 60)
1909                 off += 24 * 60;
1910
1911         return off * 60;
1912 }
1913
1914 /* calculate timezone offset */
1915 gchar *tzoffset(time_t *now)
1916 {
1917         static gchar offset_string[6];
1918         struct tm gmt, *lt;
1919         gint off;
1920         gchar sign = '+';
1921
1922         gmt = *gmtime(now);
1923         lt = localtime(now);
1924
1925         off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
1926
1927         if (lt->tm_year < gmt.tm_year)
1928                 off -= 24 * 60;
1929         else if (lt->tm_year > gmt.tm_year)
1930                 off += 24 * 60;
1931         else if (lt->tm_yday < gmt.tm_yday)
1932                 off -= 24 * 60;
1933         else if (lt->tm_yday > gmt.tm_yday)
1934                 off += 24 * 60;
1935
1936         if (off < 0) {
1937                 sign = '-';
1938                 off = -off;
1939         }
1940
1941         if (off >= 24 * 60)             /* should be impossible */
1942                 off = 23 * 60 + 59;     /* if not, insert silly value */
1943
1944         sprintf(offset_string, "%c%02d%02d", sign, off / 60, off % 60);
1945
1946         return offset_string;
1947 }
1948
1949 void get_rfc822_date(gchar *buf, gint len)
1950 {
1951         struct tm *lt;
1952         time_t t;
1953         gchar day[4], mon[4];
1954         gint dd, hh, mm, ss, yyyy;
1955
1956         t = time(NULL);
1957         lt = localtime(&t);
1958
1959         sscanf(asctime(lt), "%3s %3s %d %d:%d:%d %d\n",
1960                day, mon, &dd, &hh, &mm, &ss, &yyyy);
1961         g_snprintf(buf, len, "%s, %d %s %d %02d:%02d:%02d %s",
1962                    day, dd, mon, yyyy, hh, mm, ss, tzoffset(&t));
1963 }
1964
1965 void debug_print(const gchar *format, ...)
1966 {
1967         va_list args;
1968         gchar buf[BUFFSIZE];
1969
1970         if (!debug_mode) return;
1971
1972         va_start(args, format);
1973         g_vsnprintf(buf, sizeof(buf), format, args);
1974         va_end(args);
1975
1976         fputs(buf, stdout);
1977 }
1978
1979 void log_print(const gchar *format, ...)
1980 {
1981         va_list args;
1982         gchar buf[BUFFSIZE];
1983
1984         va_start(args, format);
1985         g_vsnprintf(buf, sizeof(buf), format, args);
1986         va_end(args);
1987
1988         if (debug_mode) fputs(buf, stdout);
1989         log_window_append(buf, LOG_NORMAL);
1990         statusbar_puts_all(buf);
1991 }
1992
1993 void log_message(const gchar *format, ...)
1994 {
1995         va_list args;
1996         gchar buf[BUFFSIZE];
1997
1998         va_start(args, format);
1999         g_vsnprintf(buf, sizeof(buf), format, args);
2000         va_end(args);
2001
2002         if (debug_mode) g_message("%s", buf);
2003         log_window_append(buf, LOG_MSG);
2004 }
2005
2006 void log_warning(const gchar *format, ...)
2007 {
2008         va_list args;
2009         gchar buf[BUFFSIZE];
2010
2011         va_start(args, format);
2012         g_vsnprintf(buf, sizeof(buf), format, args);
2013         va_end(args);
2014
2015         g_warning("%s", buf);
2016         log_window_append(buf, LOG_WARN);
2017 }
2018
2019 void log_error(const gchar *format, ...)
2020 {
2021         va_list args;
2022         gchar buf[BUFFSIZE];
2023
2024         va_start(args, format);
2025         g_vsnprintf(buf, sizeof(buf), format, args);
2026         va_end(args);
2027
2028         g_warning("%s", buf);
2029         log_window_append(buf, LOG_ERROR);
2030 }