~ubuntu-branches/ubuntu/vivid/liferea/vivid-proposed

« back to all changes in this revision

Viewing changes to src/common.c

  • Committer: Package Import Robot
  • Author(s): bojo42
  • Date: 2012-03-29 14:17:21 UTC
  • mfrom: (1.3.9) (3.2.5 sid)
  • Revision ID: package-import@ubuntu.com-20120329141721-tbfopcrc5797wxt7
Tags: 1.8.3-0.1ubuntu1
* New upstream release (LP: #290666, #371754, #741543, #716688)
* Merge from Debian unstable (LP: #935147), remaining changes:
* debian/patches:
  - drop gtk-status-icon.patch & notification-append as in upstream
  - drop fix_systray_behavior as mostly upstreamed and rest seems unused
  - 01_ubuntu_feedlists: update & rename, move planets to "Open Source"  
  - add_X-Ubuntu-Gettext-Domain: rebase
  - libunity.patch: rebase, apply before indicator patch (liferea_shell.c)
  - libindicate_increase_version.patch: exclude from libindicate.patch
  - deactivate libindicate.patch, seems partly upstreamed and needs rework
* debian/control: libindicate-dev, libindicate-gtk-dev & libunity-dev
* debian/liferea.indicate & liferea.install: ship indicator desktop file
* debian/rules: enable libindicate

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
/**
2
2
 * @file common.c common routines for Liferea
3
3
 * 
4
 
 * Copyright (C) 2003-2008  Lars Lindner <lars.lindner@gmail.com>
 
4
 * Copyright (C) 2003-2010  Lars Lindner <lars.lindner@gmail.com>
5
5
 * Copyright (C) 2004-2006  Nathan J. Conrad <t98502@users.sourceforge.net>
6
6
 * Copyright (C) 2004       Karl Soderstrom <ks@xanadunet.net>
7
7
 *
8
 
 * parts of the RFC822 timezone decoding were reused from the gmime source
9
 
 *
10
 
 *    Authors: Michael Zucchi <notzed@helixcode.com>
11
 
 *             Jeffrey Stedfast <fejj@helixcode.com>
12
 
 *
13
 
 *    Copyright 2000 Helix Code, Inc. (www.helixcode.com)
14
 
 * 
15
 
 * The date formatting was reused from the Evolution code base
16
 
 *
17
 
 *    Author: Chris Lahey <clahey@ximian.com
18
 
 *
19
 
 *    Copyright 2001, Ximian, Inc.
20
 
 *
21
8
 * This program is free software; you can redistribute it and/or modify
22
9
 * it under the terms of the GNU General Public License as published by
23
10
 * the Free Software Foundation; either version 2 of the License, or
37
24
#  include <config.h>
38
25
#endif
39
26
 
40
 
#define _XOPEN_SOURCE   600 /* glibc2 needs this (man strptime) */
41
 
 
42
27
#include <libxml/uri.h>
43
28
#include <gtk/gtk.h>
44
29
#include <glib.h>
47
32
#include <sys/stat.h>
48
33
#include <errno.h>
49
34
#include <string.h>
50
 
#include <locale.h>
51
35
#include <time.h>
52
36
#include <stdlib.h>
53
37
#include <ctype.h>
54
38
 
55
39
#include "common.h"
56
 
#include "conf.h"
57
 
#include "e-date.h"
58
40
#include "feed.h"
59
41
#include "debug.h"
60
42
 
61
43
static gchar *lifereaUserPath = NULL;
62
44
 
63
 
/* Conversion function which should be applied to all read XML strings, 
64
 
   to ensure proper UTF8. This is because we use libxml2 in recovery
65
 
   mode which can produce invalid UTF-8. 
66
 
   
67
 
   The valid or a corrected string is returned. The original XML 
68
 
   string is modified . */
69
 
gchar * common_utf8_fix(gchar *string) {
70
 
        const gchar     *invalid_offset;
71
 
 
72
 
        if(NULL == string)
73
 
                return NULL;
74
 
                
75
 
        if(!g_utf8_validate(string, -1, &invalid_offset)) {
76
 
                /* if we have an invalid string we try to shorten
77
 
                   it until it is valid UTF-8 */
78
 
                debug0(DEBUG_PARSING, "parser delivered invalid UTF-8!");
79
 
                debug1(DEBUG_PARSING, " >>>%s<<<", string);
80
 
                debug1(DEBUG_PARSING, "first invalid char is: >>>%s<<<" , invalid_offset);
81
 
                debug0(DEBUG_PARSING, "removing invalid bytes");
82
 
                
83
 
                do {
84
 
                        memmove((void *)invalid_offset, invalid_offset + 1, strlen(invalid_offset + 1) + 1);                    
85
 
                } while(!g_utf8_validate(string, -1, &invalid_offset));
86
 
                
87
 
                debug0(DEBUG_PARSING, "result is:");
88
 
                debug1(DEBUG_PARSING, " >>>%s<<<", string);
89
 
        }
90
 
        
91
 
        return string;
92
 
}
93
 
 
94
 
long common_parse_long(gchar *str, long def) {
 
45
long
 
46
common_parse_long (const gchar *str, long def)
 
47
{
95
48
        long num;
96
49
 
97
 
        if(str == NULL)
 
50
        if (str == NULL)
98
51
                return def;
99
 
        if(0 == (sscanf(str,"%ld",&num)))
 
52
        if (0 == (sscanf (str,"%ld", &num)))
100
53
                num = def;
101
54
        
102
55
        return num;
103
56
}
104
57
 
105
 
#define TIMESTRLEN      256
106
 
 
107
 
gchar * common_format_date(time_t date, const gchar *date_format) {
108
 
        gchar           *result;
109
 
        struct tm       date_tm;
110
 
        
111
 
        if (date == 0) {
112
 
                return g_strdup ("");
113
 
        }
114
 
        
115
 
        localtime_r (&date, &date_tm);
116
 
        
117
 
        result = g_new0(gchar, TIMESTRLEN);
118
 
        e_utf8_strftime_fix_am_pm(result, TIMESTRLEN, date_format, &date_tm);
119
 
        return result;
120
 
}
121
 
 
122
 
/* This function is originally from the Evolution 2.6.2 code (e-cell-date.c) */
123
 
gchar * common_format_nice_date(time_t date) {
124
 
        time_t nowdate = time(NULL);
125
 
        time_t yesdate;
126
 
        struct tm then, now, yesterday;
127
 
        gchar *temp, *buf;
128
 
        gboolean done = FALSE;
129
 
        
130
 
        if (date == 0) {
131
 
                return g_strdup ("");
132
 
        }
133
 
        
134
 
        buf = g_new0(gchar, TIMESTRLEN + 1);
135
 
 
136
 
        localtime_r (&date, &then);
137
 
        localtime_r (&nowdate, &now);
138
 
 
139
 
/*      if (nowdate - date < 60 * 60 * 8 && nowdate > date) {
140
 
                e_utf8_strftime_fix_am_pm (buf, TIMESTRLEN, _("%l:%M %p"), &then);
141
 
                done = TRUE;
142
 
        }*/
143
 
 
144
 
        if (!done) {
145
 
                if (then.tm_mday == now.tm_mday &&
146
 
                    then.tm_mon == now.tm_mon &&
147
 
                    then.tm_year == now.tm_year) {
148
 
                        /* translation hint: date format for today, reorder format codes as necessary */
149
 
                        e_utf8_strftime_fix_am_pm (buf, TIMESTRLEN, _("Today %l:%M %p"), &then);
150
 
                        done = TRUE;
151
 
                }
152
 
        }
153
 
        if (!done) {
154
 
                yesdate = nowdate - 60 * 60 * 24;
155
 
                localtime_r (&yesdate, &yesterday);
156
 
                if (then.tm_mday == yesterday.tm_mday &&
157
 
                    then.tm_mon == yesterday.tm_mon &&
158
 
                    then.tm_year == yesterday.tm_year) {
159
 
                        /* translation hint: date format for yesterday, reorder format codes as necessary */
160
 
                        e_utf8_strftime_fix_am_pm (buf, TIMESTRLEN, _("Yesterday %l:%M %p"), &then);
161
 
                        done = TRUE;
162
 
                }
163
 
        }
164
 
        if (!done) {
165
 
                int i;
166
 
                for (i = 2; i < 7; i++) {
167
 
                        yesdate = nowdate - 60 * 60 * 24 * i;
168
 
                        localtime_r (&yesdate, &yesterday);
169
 
                        if (then.tm_mday == yesterday.tm_mday &&
170
 
                            then.tm_mon == yesterday.tm_mon &&
171
 
                            then.tm_year == yesterday.tm_year) {
172
 
                                /* translation hint: date format for dates older than 2 days but not older than a week, reorder format codes as necessary */
173
 
                                e_utf8_strftime_fix_am_pm (buf, TIMESTRLEN, _("%a %l:%M %p"), &then);
174
 
                                done = TRUE;
175
 
                                break;
176
 
                        }
177
 
                }
178
 
        }
179
 
        if (!done) {
180
 
                if (then.tm_year == now.tm_year) {
181
 
                        /* translation hint: date format for dates older than a week but from this year, reorder format codes as necessary */
182
 
                        e_utf8_strftime_fix_am_pm (buf, TIMESTRLEN, _("%b %d %l:%M %p"), &then);
183
 
                } else {
184
 
                        /* translation hint: date format for dates from the last years, reorder format codes as necessary */
185
 
                        e_utf8_strftime_fix_am_pm (buf, TIMESTRLEN, _("%b %d %Y"), &then);
186
 
                }
187
 
        }
188
 
 
189
 
        temp = buf;
190
 
        while ((temp = strstr (temp, "  "))) {
191
 
                memmove (temp, temp + 1, strlen (temp));
192
 
        }
193
 
        temp = g_strstrip (buf);
194
 
        return temp;
195
 
}
196
 
 
197
 
/* converts a ISO 8601 time string to a time_t value */
198
 
time_t parseISO8601Date(gchar *date) {
199
 
        struct tm       tm;
200
 
        time_t          t, t2, offset = 0;
201
 
        gboolean        success = FALSE;
202
 
        gchar *pos;
203
 
        
204
 
        g_assert(date != NULL);
205
 
        
206
 
        memset(&tm, 0, sizeof(struct tm));
207
 
        
208
 
        /* we expect at least something like "2003-08-07T15:28:19" and
209
 
           don't require the second fractions and the timezone info
210
 
 
211
 
           the most specific format:   YYYY-MM-DDThh:mm:ss.sTZD
212
 
         */
213
 
         
214
 
        /* full specified variant */
215
 
        if(NULL != (pos = strptime((const char *)date, "%t%Y-%m-%dT%H:%M%t", &tm))) {
216
 
                /* Parse seconds */
217
 
                if (*pos == ':')
218
 
                        pos++;
219
 
                if (isdigit(pos[0]) && !isdigit(pos[1])) {
220
 
                        tm.tm_sec = pos[0] - '0';
221
 
                        pos++;
222
 
                } else if (isdigit(pos[0]) && isdigit(pos[1])) {
223
 
                        tm.tm_sec = 10*(pos[0]-'0') + pos[1] - '0';
224
 
                        pos +=2;
225
 
                }
226
 
                /* Parse second fractions */
227
 
                if (*pos == '.') {
228
 
                        while (*pos == '.' || isdigit(pos[0]))
229
 
                                pos++;
230
 
                }
231
 
                /* Parse timezone */
232
 
                if (*pos == 'Z')
233
 
                        offset = 0;
234
 
                else if ((*pos == '+' || *pos == '-') && isdigit(pos[1]) && isdigit(pos[2]) && strlen(pos) >= 3) {
235
 
                        offset = (10*(pos[1] - '0') + (pos[2] - '0')) * 60 * 60;
236
 
                        
237
 
                        if (pos[3] == ':' && isdigit(pos[4]) && isdigit(pos[5]))
238
 
                                offset +=  (10*(pos[4] - '0') + (pos[5] - '0')) * 60;
239
 
                        else if (isdigit(pos[3]) && isdigit(pos[4]))
240
 
                                offset +=  (10*(pos[3] - '0') + (pos[4] - '0')) * 60;
241
 
                        
242
 
                        offset *= (pos[0] == '+') ? 1 : -1;
243
 
 
244
 
                }
245
 
                success = TRUE;
246
 
        /* only date */
247
 
        } else if(NULL != strptime((const char *)date, "%t%Y-%m-%d", &tm))
248
 
                success = TRUE;
249
 
        /* there were others combinations too... */
250
 
 
251
 
        if(TRUE == success) {
252
 
                if((time_t)(-1) != (t = mktime(&tm))) {
253
 
                        /* Correct for the local timezone*/
254
 
                        struct tm tmp_tm;
255
 
                        
256
 
                        t = t - offset;
257
 
                        gmtime_r(&t, &tmp_tm);
258
 
                        t2 = mktime(&tmp_tm);
259
 
                        t = t - (t2 - t);
260
 
                        
261
 
                        return t;
262
 
                } else {
263
 
                        debug0(DEBUG_PARSING, "internal error! time conversion error! mktime failed!");
264
 
                }
265
 
        } else {
266
 
                debug0(DEBUG_PARSING, "Invalid ISO8601 date format! Ignoring <dc:date> information!");
267
 
        }
268
 
        
269
 
        return 0;
270
 
}
271
 
 
272
 
static const gchar *dayofweek[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
273
 
static const gchar *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
274
 
 
275
 
gchar *createRFC822Date(const time_t *time) {
276
 
        struct tm *tm;
277
 
 
278
 
        tm = gmtime(time); /* No need to free because it is statically allocated */
279
 
        return g_strdup_printf("%s, %2d %s %4d %02d:%02d:%02d GMT", dayofweek[tm->tm_wday], tm->tm_mday,
280
 
                                           months[tm->tm_mon], 1900 + tm->tm_year, tm->tm_hour, tm->tm_min, tm->tm_sec);
281
 
}
282
 
 
283
 
/* in theory, we'd need only the RFC822 timezones here
284
 
   in practice, feeds also use other timezones...        */
285
 
static struct {
286
 
        const char *name;
287
 
        int offset;
288
 
} tz_offsets [] = {
289
 
        { "IDLW", -1200 },
290
 
        { "HAST", -1000 },
291
 
        { "AKST", -900 },
292
 
        { "AKDT", -800 },
293
 
        { "WESZ", 100 },
294
 
        { "WEST", 100 },
295
 
        { "WEDT", 100 },
296
 
        { "MEST", 200 },
297
 
        { "MESZ", 200 },
298
 
        { "CEST", 200 },
299
 
        { "CEDT", 200 },
300
 
        { "EEST", 300 },
301
 
        { "EEDT", 300 },
302
 
        { "IRST", 430 },
303
 
        { "CNST", 800 },
304
 
        { "ACST", 930 },
305
 
        { "ACDT", 1030 },
306
 
        { "AEST", 1000 },
307
 
        { "AEDT", 1100 },
308
 
        { "IDLE", 1200 },
309
 
        { "NZST", 1200 },
310
 
        { "NZDT", 1300 },
311
 
        { "GMT", 0 },
312
 
        { "EST", -500 },
313
 
        { "EDT", -400 },
314
 
        { "CST", -600 },
315
 
        { "CDT", -500 },
316
 
        { "MST", -700 },
317
 
        { "MDT", -600 },
318
 
        { "PST", -800 },
319
 
        { "PDT", -700 },
320
 
        { "HDT", -900 },
321
 
        { "YST", -900 },
322
 
        { "YDT", -800 },
323
 
        { "AST", -400 },
324
 
        { "ADT", -300 },
325
 
        { "VST", -430 },
326
 
        { "NST", -330 },
327
 
        { "NDT", -230 },
328
 
        { "WET", 0 },
329
 
        { "WEZ", 0 },
330
 
        { "IST", 100 },
331
 
        { "CET", 100 },
332
 
        { "MEZ", 100 },
333
 
        { "EET", 200 },
334
 
        { "MSK", 300 },
335
 
        { "MSD", 400 },
336
 
        { "IRT", 330 },
337
 
        { "IST", 530 },
338
 
        { "ICT", 700 },
339
 
        { "JST", 900 },
340
 
        { "NFT", 1130 },
341
 
        { "UT", 0 },
342
 
        { "PT", -800 },
343
 
        { "BT", 300 },
344
 
        { "Z", 0 },
345
 
        { "A", -100 },
346
 
        { "M", -1200 },
347
 
        { "N", 100 },
348
 
        { "Y", 1200 }
349
 
};
350
 
/** @returns timezone offset in seconds */
351
 
static time_t common_parse_rfc822_tz(char *token) {
352
 
        int offset = 0;
353
 
        const char *inptr = token;
354
 
        int num_timezones = sizeof(tz_offsets) / sizeof((tz_offsets)[0]);
355
 
 
356
 
        if (*inptr == '+' || *inptr == '-') {
357
 
                offset = atoi (inptr);
358
 
        } else {
359
 
                int t;
360
 
 
361
 
                if (*inptr == '(')
362
 
                        inptr++;
363
 
 
364
 
                for (t = 0; t < num_timezones; t++)
365
 
                        if (!strncmp (inptr, tz_offsets[t].name, strlen (tz_offsets[t].name))) {
366
 
                                offset = tz_offsets[t].offset;
367
 
                                break;
368
 
                        }
369
 
        }
370
 
        
371
 
        return 60 * ((offset / 100) * 60 + (offset % 100));
372
 
}
373
 
 
374
 
 
375
 
/* converts a RFC822 time string to a time_t value */
376
 
time_t parseRFC822Date(gchar *date) {
377
 
        struct tm       tm;
378
 
        time_t          t, t2;
379
 
        char            *oldlocale;
380
 
        char            *pos;
381
 
        gboolean        success = FALSE;
382
 
 
383
 
        memset(&tm, 0, sizeof(struct tm));
384
 
 
385
 
        /* we expect at least something like "03 Dec 12 01:38:34" 
386
 
           and don't require a day of week or the timezone
387
 
 
388
 
           the most specific format we expect:  "Fri, 03 Dec 12 01:38:34 CET"
389
 
         */
390
 
        /* skip day of week */
391
 
        if(NULL != (pos = g_utf8_strchr(date, -1, ',')))
392
 
                date = ++pos;
393
 
 
394
 
        /* we expect English month names, so we set the locale */
395
 
        oldlocale = g_strdup(setlocale(LC_TIME, NULL));
396
 
        setlocale(LC_TIME, "C");
397
 
        
398
 
        /* standard format with seconds and 4 digit year */
399
 
        if(NULL != (pos = strptime((const char *)date, " %d %b %Y %T", &tm)))
400
 
                success = TRUE;
401
 
        /* non-standard format without seconds and 4 digit year */
402
 
        else if(NULL != (pos = strptime((const char *)date, " %d %b %Y %H:%M", &tm)))
403
 
                success = TRUE;
404
 
        /* non-standard format with seconds and 2 digit year */
405
 
        else if(NULL != (pos = strptime((const char *)date, " %d %b %y %T", &tm)))
406
 
                success = TRUE;
407
 
        /* non-standard format without seconds 2 digit year */
408
 
        else if(NULL != (pos = strptime((const char *)date, " %d %b %y %H:%M", &tm)))
409
 
                success = TRUE;
410
 
        
411
 
        while(pos && *pos != '\0' && isspace((int)*pos))       /* skip whitespaces before timezone */
412
 
                pos++;
413
 
        
414
 
        if(oldlocale) {
415
 
                setlocale(LC_TIME, oldlocale);  /* and reset it again */
416
 
                g_free(oldlocale);
417
 
        }
418
 
        
419
 
        if(success) {
420
 
                if((time_t)(-1) != (t = mktime(&tm))) {
421
 
                        /* GMT time, with no daylight savings time
422
 
                           correction. (Usually, there is no daylight savings
423
 
                           time since the input is GMT.) */
424
 
                        t = t - common_parse_rfc822_tz(pos);
425
 
                        t2 = mktime(gmtime(&t));
426
 
                        t = t - (t2 - t);
427
 
                        return t;
428
 
                } else {
429
 
                        debug0(DEBUG_PARSING, "internal error! time conversion error! mktime failed!");
430
 
                }
431
 
        }
432
 
        
433
 
        return 0;
434
 
}
435
 
 
436
58
static void
437
59
common_check_dir (gchar *path)
438
60
{
449
71
{
450
72
        gchar *cachePath;
451
73
 
452
 
        lifereaUserPath = g_build_filename (g_get_home_dir(), ".liferea_1.6", NULL);
 
74
        lifereaUserPath = g_build_filename (g_get_home_dir(), ".liferea_1.8", NULL);
453
75
        cachePath = g_build_filename (lifereaUserPath, "cache", NULL);
454
76
 
455
77
        common_check_dir (g_strdup (lifereaUserPath));
466
88
        umask (077);
467
89
}
468
90
 
469
 
const gchar * common_get_cache_path(void) {
470
 
        
471
 
        if(!lifereaUserPath)
472
 
                common_init_cache_path();
 
91
const gchar *
 
92
common_get_cache_path (void)
 
93
{       
 
94
        if (!lifereaUserPath)
 
95
                common_init_cache_path ();
473
96
                
474
97
        return lifereaUserPath;
475
98
}
476
99
 
477
 
gchar * common_create_cache_filename(const gchar *folder, const gchar *filename, const gchar *extension) {
 
100
gchar *
 
101
common_create_cache_filename (const gchar *folder, const gchar *filename, const gchar *extension)
 
102
{
478
103
        gchar *result;
479
104
 
480
 
        result = g_strdup_printf("%s" G_DIR_SEPARATOR_S "%s%s%s%s%s", common_get_cache_path(),
481
 
                                 (folder != NULL) ? folder : "",
482
 
                                 (folder != NULL) ? G_DIR_SEPARATOR_S : "",
483
 
                                 filename,
484
 
                                 (extension != NULL)? "." : "",
485
 
                                 (extension != NULL)? extension : "");
 
105
        result = g_strdup_printf ("%s" G_DIR_SEPARATOR_S "%s%s%s%s%s", common_get_cache_path (),
 
106
                                  folder ? folder : "",
 
107
                                  folder ? G_DIR_SEPARATOR_S : "",
 
108
                                  filename,
 
109
                                  extension ? "." : "",
 
110
                                  extension ? extension : "");
486
111
 
487
112
        return result;
488
113
}
489
114
 
490
 
xmlChar * common_uri_escape(const xmlChar *url) {
491
 
        xmlChar *result;
492
 
        
 
115
xmlChar *
 
116
common_uri_escape (const xmlChar *url)
 
117
{
 
118
        xmlChar *result, *tmp;
 
119
 
 
120
        g_assert (NULL != url);
 
121
                
493
122
        /* xmlURIEscape returns NULL if spaces are in the URL, 
494
123
           so we need to replace them first (see SF #2965158) */
495
 
        result = common_strreplace (g_strdup (url), " ", "+");
496
 
 
497
 
        result = xmlURIEscape(result);
 
124
        tmp = common_strreplace (g_strdup (url), " ", "+");
 
125
        result = xmlURIEscape (tmp);
 
126
        g_free (tmp);
498
127
        
499
128
        /* workaround if escaping somehow fails... */
500
 
        if(NULL == result)
501
 
                result = g_strdup(url);
 
129
        if (!result)
 
130
                result = g_strdup (url);
502
131
 
503
132
        return result;  
504
133
}
505
134
 
506
 
xmlChar * common_uri_unescape(const xmlChar *url) {
507
 
 
508
 
        return xmlURIUnescapeString(url, -1, NULL);
 
135
xmlChar *
 
136
common_uri_unescape (const xmlChar *url)
 
137
{
 
138
        return xmlURIUnescapeString (url, -1, NULL);
 
139
}
 
140
 
 
141
xmlChar *
 
142
common_uri_sanitize (const xmlChar *uri)
 
143
{
 
144
        xmlChar *tmp, *result;
 
145
        
 
146
        /* We must escape all dangerous characters (e.g. & and spaces)
 
147
           in the URL. As we do not know if the URL is already escaped we
 
148
           simply unescape and reescape it. */
 
149
        tmp = common_uri_unescape (uri);
 
150
        result = common_uri_escape (tmp);
 
151
        g_free (tmp);
 
152
 
 
153
        return result;
509
154
}
510
155
 
511
156
/* to correctly escape and expand URLs */
512
 
xmlChar * common_build_url(const gchar *url, const gchar *baseURL) {
 
157
xmlChar *
 
158
common_build_url (const gchar *url, const gchar *baseURL)
 
159
{
513
160
        xmlChar *escapedURL, *absURL, *escapedBaseURL;
514
161
 
515
 
        escapedURL = common_uri_escape(url);
 
162
        escapedURL = common_uri_escape (url);
516
163
 
517
 
        if(NULL != baseURL) {
518
 
                escapedBaseURL = common_uri_escape(baseURL);    
519
 
                absURL = xmlBuildURI(escapedURL, escapedBaseURL);
520
 
                if(absURL)
521
 
                        xmlFree(escapedURL);
 
164
        if (baseURL) {
 
165
                escapedBaseURL = common_uri_escape (baseURL);   
 
166
                absURL = xmlBuildURI (escapedURL, escapedBaseURL);
 
167
                if (absURL)
 
168
                        xmlFree (escapedURL);
522
169
                else
523
170
                        absURL = escapedURL;
524
 
 
525
 
                xmlFree(escapedBaseURL);
 
171
                
 
172
                xmlFree (escapedBaseURL);
526
173
        } else {
527
174
                absURL = escapedURL;
528
175
        }
530
177
        return absURL;
531
178
}
532
179
 
533
 
const gchar * common_get_direction_mark(gchar *text) {
534
 
        PangoDirection          pango_direction = PANGO_DIRECTION_NEUTRAL;
 
180
/*
 
181
 * Returns a string that can be used for the HTML "dir" attribute.
 
182
 * Direction is taken from a string, regardless of any language tags.
 
183
 */
 
184
const gchar *
 
185
common_get_text_direction (const gchar *text)
 
186
{
 
187
        PangoDirection pango_direction = PANGO_DIRECTION_NEUTRAL;
 
188
        
 
189
        if (text)
 
190
                pango_direction = pango_find_base_dir (text, -1);
 
191
 
 
192
        if (pango_direction == PANGO_DIRECTION_RTL)
 
193
                return ("rtl");
 
194
        else
 
195
                return ("ltr");
 
196
}
 
197
 
 
198
const gchar *
 
199
common_get_app_direction (void)
 
200
{
535
201
        GtkTextDirection        gtk_direction;
536
 
        
537
 
        if(text)
538
 
                pango_direction = pango_find_base_dir(text, -1);
539
 
                
540
 
        switch(pango_direction) {
541
 
                case PANGO_DIRECTION_LTR:
542
 
                        gtk_direction = GTK_TEXT_DIR_LTR;
543
 
                        break;
544
 
                case PANGO_DIRECTION_RTL:
545
 
                        gtk_direction = GTK_TEXT_DIR_RTL;
546
 
                        break;
547
 
                default:
548
 
                        gtk_direction = gtk_widget_get_default_direction();
549
 
                        break;
550
 
        }
551
202
 
552
 
        switch(gtk_direction) {
553
 
                case GTK_TEXT_DIR_RTL: 
554
 
                        return "\342\200\217"; /* U+200F RIGHT-TO-LEFT MARK */
555
 
                case GTK_TEXT_DIR_LTR: 
556
 
                        return "\342\200\216"; /* U+200E LEFT-TO-RIGHT MARK */
557
 
                default:
558
 
                        return "";
559
 
        }
 
203
        gtk_direction = gtk_widget_get_default_direction ();
 
204
        if (gtk_direction == GTK_TEXT_DIR_RTL)
 
205
                return ("rtl");
 
206
        else
 
207
                return ("ltr");
560
208
}
561
209
 
562
210
#ifndef HAVE_STRSEP
563
211
/* code taken from glibc-2.2.1/sysdeps/generic/strsep.c */
564
 
char * common_strsep (char **stringp, const char *delim) {
 
212
char *
 
213
common_strsep (char **stringp, const char *delim)
 
214
{
565
215
        char *begin, *end;
566
216
 
567
217
        begin = *stringp;
606
256
 
607
257
/* Taken from gaim 24 June 2004, copyrighted by the gaim developers
608
258
   under the GPL, etc.... It was slightly modified to free the passed string */
609
 
gchar * common_strreplace(gchar *string, const gchar *delimiter, const gchar *replacement) {
 
259
gchar *
 
260
common_strreplace (gchar *string, const gchar *delimiter, const gchar *replacement)
 
261
{
610
262
        gchar **split;
611
263
        gchar *ret;
612
264
 
613
 
        g_return_val_if_fail(string      != NULL, NULL);
614
 
        g_return_val_if_fail(delimiter   != NULL, NULL);
615
 
        g_return_val_if_fail(replacement != NULL, NULL);
 
265
        g_return_val_if_fail (string      != NULL, NULL);
 
266
        g_return_val_if_fail (delimiter   != NULL, NULL);
 
267
        g_return_val_if_fail (replacement != NULL, NULL);
616
268
 
617
 
        split = g_strsplit(string, delimiter, 0);
618
 
        ret = g_strjoinv(replacement, split);
619
 
        g_strfreev(split);
620
 
        g_free(string);
 
269
        split = g_strsplit (string, delimiter, 0);
 
270
        ret = g_strjoinv (replacement, split);
 
271
        g_strfreev (split);
 
272
        g_free (string);
621
273
 
622
274
        return ret;
623
275
}
627
279
/* strcasestr is Copyright (C) 1994, 1996-2000, 2004 Free Software
628
280
   Foundation, Inc.  It was taken from the GNU C Library, which is
629
281
   licenced under the GPL v2.1 or (at your option) newer version. */
630
 
char *common_strcasestr (const char *phaystack, const char *pneedle)
 
282
char *
 
283
common_strcasestr (const char *phaystack, const char *pneedle)
631
284
{
632
285
        register const unsigned char *haystack, *needle;
633
286
        register chartype b, c;
719
372
                return 0;
720
373
        }
721
374
}
 
375
 
 
376
gchar *
 
377
common_get_localized_filename (const gchar *str)
 
378
{
 
379
        const gchar *const *languages = g_get_language_names();
 
380
        int i = 0;
 
381
 
 
382
        while (languages[i] != NULL) {
 
383
                gchar *filename = g_strdup_printf (str, strcmp (languages[i], "C") ? languages[i] : "en");
 
384
 
 
385
                if (g_file_test (filename, G_FILE_TEST_IS_REGULAR))
 
386
                        return filename;
 
387
 
 
388
                g_free (filename);
 
389
                i++;
 
390
        }
 
391
 
 
392
        g_warning ("No file found for %s", str);
 
393
 
 
394
        return NULL;
 
395
}
 
396
 
 
397
void
 
398
common_copy_file (const gchar *src, const gchar *dest)
 
399
{
 
400
        gchar   *content;
 
401
        gsize   length;
 
402
 
 
403
        if (g_file_get_contents (src, &content, &length, NULL))
 
404
                g_file_set_contents (dest, content, length, NULL);
 
405
        g_free (content);
 
406
}