14192
|
1 /**
|
|
2 * @file gaim-desktop-item.c Functions for managing .desktop files
|
|
3 * @ingroup core
|
|
4 *
|
|
5 * Gaim is the legal property of its developers, whose names are too numerous
|
|
6 * to list here. Please refer to the COPYRIGHT file distributed with this
|
|
7 * source distribution.
|
|
8 *
|
|
9 * This program is free software; you can redistribute it and/or modify
|
|
10 * it under the terms of the GNU General Public License as published by
|
|
11 * the Free Software Foundation; either version 2 of the License, or
|
|
12 * (at your option) any later version.
|
|
13 *
|
|
14 * This program is distributed in the hope that it will be useful,
|
|
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
17 * GNU General Public License for more details.
|
|
18 *
|
|
19 * You should have received a copy of the GNU General Public License
|
|
20 * along with this program; if not, write to the Free Software
|
|
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
22 *
|
|
23 */
|
|
24
|
|
25 /*
|
|
26 * The following code has been adapted from gnome-desktop-item.[ch],
|
|
27 * as found on gnome-desktop-2.8.1.
|
|
28 *
|
|
29 * Copyright (C) 2004 by Alceste Scalas <alceste.scalas@gmx.net>.
|
|
30 *
|
|
31 * Original copyright notice:
|
|
32 *
|
|
33 * Copyright (C) 1999, 2000 Red Hat Inc.
|
|
34 * Copyright (C) 2001 Sid Vicious
|
|
35 * All rights reserved.
|
|
36 *
|
|
37 * This file is part of the Gnome Library.
|
|
38 *
|
|
39 * The Gnome Library is free software; you can redistribute it and/or
|
|
40 * modify it under the terms of the GNU Library General Public License as
|
|
41 * published by the Free Software Foundation; either version 2 of the
|
|
42 * License, or (at your option) any later version.
|
|
43 *
|
|
44 * The Gnome Library is distributed in the hope that it will be useful,
|
|
45 * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
46 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
47 * Library General Public License for more details.
|
|
48 *
|
|
49 * You should have received a copy of the GNU Library General Public
|
|
50 * License along with the Gnome Library; see the file COPYING.LIB. If not,
|
|
51 * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
|
52 * Boston, MA 02111-1307, USA.
|
|
53 */
|
|
54
|
|
55 #include <errno.h>
|
|
56 #include <stdio.h>
|
|
57 #include <string.h>
|
|
58 #include <time.h>
|
|
59 #include "desktopitem.h"
|
|
60 #include "internal.h"
|
|
61
|
|
62 struct _GaimDesktopItem {
|
|
63 int refcount;
|
|
64
|
|
65 /* all languages used */
|
|
66 GList *languages;
|
|
67
|
|
68 GaimDesktopItemType type;
|
|
69
|
|
70 /* `modified' means that the ditem has been
|
|
71 * modified since the last save. */
|
|
72 gboolean modified;
|
|
73
|
|
74 /* Keys of the main section only */
|
|
75 GList *keys;
|
|
76
|
|
77 GList *sections;
|
|
78
|
|
79 /* This includes ALL keys, including
|
|
80 * other sections, separated by '/' */
|
|
81 GHashTable *main_hash;
|
|
82
|
|
83 char *location;
|
|
84
|
|
85 time_t mtime;
|
|
86 };
|
|
87
|
|
88 typedef struct {
|
|
89 char *name;
|
|
90 GList *keys;
|
|
91 } Section;
|
|
92
|
|
93 typedef enum {
|
|
94 ENCODING_UNKNOWN,
|
|
95 ENCODING_UTF8,
|
|
96 ENCODING_LEGACY_MIXED
|
|
97 } Encoding;
|
|
98
|
|
99 /**************************************************************************
|
|
100 * Private utility functions
|
|
101 **************************************************************************/
|
|
102 static GaimDesktopItemType
|
|
103 type_from_string (const char *type)
|
|
104 {
|
|
105 if (!type)
|
|
106 return GAIM_DESKTOP_ITEM_TYPE_NULL;
|
|
107
|
|
108 switch (type [0]) {
|
|
109 case 'A':
|
|
110 if (!strcmp (type, "Application"))
|
|
111 return GAIM_DESKTOP_ITEM_TYPE_APPLICATION;
|
|
112 break;
|
|
113 case 'L':
|
|
114 if (!strcmp (type, "Link"))
|
|
115 return GAIM_DESKTOP_ITEM_TYPE_LINK;
|
|
116 break;
|
|
117 case 'F':
|
|
118 if (!strcmp (type, "FSDevice"))
|
|
119 return GAIM_DESKTOP_ITEM_TYPE_FSDEVICE;
|
|
120 break;
|
|
121 case 'M':
|
|
122 if (!strcmp (type, "MimeType"))
|
|
123 return GAIM_DESKTOP_ITEM_TYPE_MIME_TYPE;
|
|
124 break;
|
|
125 case 'D':
|
|
126 if (!strcmp (type, "Directory"))
|
|
127 return GAIM_DESKTOP_ITEM_TYPE_DIRECTORY;
|
|
128 break;
|
|
129 case 'S':
|
|
130 if (!strcmp (type, "Service"))
|
|
131 return GAIM_DESKTOP_ITEM_TYPE_SERVICE;
|
|
132
|
|
133 else if (!strcmp (type, "ServiceType"))
|
|
134 return GAIM_DESKTOP_ITEM_TYPE_SERVICE_TYPE;
|
|
135 break;
|
|
136 default:
|
|
137 break;
|
|
138 }
|
|
139
|
|
140 return GAIM_DESKTOP_ITEM_TYPE_OTHER;
|
|
141 }
|
|
142
|
|
143 static Section *
|
|
144 find_section (GaimDesktopItem *item, const char *section)
|
|
145 {
|
|
146 GList *li;
|
|
147 Section *sec;
|
|
148
|
|
149 if (section == NULL)
|
|
150 return NULL;
|
|
151 if (strcmp (section, "Desktop Entry") == 0)
|
|
152 return NULL;
|
|
153
|
|
154 for (li = item->sections; li != NULL; li = li->next) {
|
|
155 sec = li->data;
|
|
156 if (strcmp (sec->name, section) == 0)
|
|
157 return sec;
|
|
158 }
|
|
159
|
|
160 sec = g_new0 (Section, 1);
|
|
161 sec->name = g_strdup (section);
|
|
162 sec->keys = NULL;
|
|
163
|
|
164 item->sections = g_list_append (item->sections, sec);
|
|
165
|
|
166 /* Don't mark the item modified, this is just an empty section,
|
|
167 * it won't be saved even */
|
|
168
|
|
169 return sec;
|
|
170 }
|
|
171
|
|
172 static Section *
|
|
173 section_from_key (GaimDesktopItem *item, const char *key)
|
|
174 {
|
|
175 char *p;
|
|
176 char *name;
|
|
177 Section *sec;
|
|
178
|
|
179 if (key == NULL)
|
|
180 return NULL;
|
|
181
|
|
182 p = strchr (key, '/');
|
|
183 if (p == NULL)
|
|
184 return NULL;
|
|
185
|
|
186 name = g_strndup (key, p - key);
|
|
187
|
|
188 sec = find_section (item, name);
|
|
189
|
|
190 g_free (name);
|
|
191
|
|
192 return sec;
|
|
193 }
|
|
194
|
|
195 static const char *
|
|
196 key_basename (const char *key)
|
|
197 {
|
|
198 char *p = strrchr (key, '/');
|
|
199 if (p != NULL)
|
|
200 return p+1;
|
|
201 else
|
|
202 return key;
|
|
203 }
|
|
204
|
|
205 static void
|
|
206 set (GaimDesktopItem *item, const char *key, const char *value)
|
|
207 {
|
|
208 Section *sec = section_from_key (item, key);
|
|
209
|
|
210 if (sec != NULL) {
|
|
211 if (value != NULL) {
|
|
212 if (g_hash_table_lookup (item->main_hash, key) == NULL)
|
|
213 sec->keys = g_list_append
|
|
214 (sec->keys,
|
|
215 g_strdup (key_basename (key)));
|
|
216
|
|
217 g_hash_table_replace (item->main_hash,
|
|
218 g_strdup (key),
|
|
219 g_strdup (value));
|
|
220 } else {
|
|
221 GList *list = g_list_find_custom
|
|
222 (sec->keys, key_basename (key),
|
|
223 (GCompareFunc)strcmp);
|
|
224 if (list != NULL) {
|
|
225 g_free (list->data);
|
|
226 sec->keys =
|
|
227 g_list_delete_link (sec->keys, list);
|
|
228 }
|
|
229 g_hash_table_remove (item->main_hash, key);
|
|
230 }
|
|
231 } else {
|
|
232 if (value != NULL) {
|
|
233 if (g_hash_table_lookup (item->main_hash, key) == NULL)
|
|
234 item->keys = g_list_append (item->keys,
|
|
235 g_strdup (key));
|
|
236
|
|
237 g_hash_table_replace (item->main_hash,
|
|
238 g_strdup (key),
|
|
239 g_strdup (value));
|
|
240 } else {
|
|
241 GList *list = g_list_find_custom
|
|
242 (item->keys, key, (GCompareFunc)strcmp);
|
|
243 if (list != NULL) {
|
|
244 g_free (list->data);
|
|
245 item->keys =
|
|
246 g_list_delete_link (item->keys, list);
|
|
247 }
|
|
248 g_hash_table_remove (item->main_hash, key);
|
|
249 }
|
|
250 }
|
|
251 item->modified = TRUE;
|
|
252 }
|
|
253
|
|
254
|
|
255 static void
|
|
256 _gaim_desktop_item_set_string (GaimDesktopItem *item,
|
|
257 const char *attr,
|
|
258 const char *value)
|
|
259 {
|
|
260 g_return_if_fail (item != NULL);
|
|
261 g_return_if_fail (item->refcount > 0);
|
|
262 g_return_if_fail (attr != NULL);
|
|
263
|
|
264 set (item, attr, value);
|
|
265
|
|
266 if (strcmp (attr, GAIM_DESKTOP_ITEM_TYPE) == 0)
|
|
267 item->type = type_from_string (value);
|
|
268 }
|
|
269
|
|
270 static GaimDesktopItem *
|
|
271 _gaim_desktop_item_new (void)
|
|
272 {
|
|
273 GaimDesktopItem *retval;
|
|
274
|
|
275 retval = g_new0 (GaimDesktopItem, 1);
|
|
276
|
|
277 retval->refcount++;
|
|
278
|
|
279 retval->main_hash = g_hash_table_new_full (g_str_hash, g_str_equal,
|
|
280 (GDestroyNotify) g_free,
|
|
281 (GDestroyNotify) g_free);
|
|
282
|
|
283 /* These are guaranteed to be set */
|
|
284 _gaim_desktop_item_set_string (retval,
|
|
285 GAIM_DESKTOP_ITEM_NAME,
|
|
286 _("No name"));
|
|
287 _gaim_desktop_item_set_string (retval,
|
|
288 GAIM_DESKTOP_ITEM_ENCODING,
|
|
289 "UTF-8");
|
|
290 _gaim_desktop_item_set_string (retval,
|
|
291 GAIM_DESKTOP_ITEM_VERSION,
|
|
292 "1.0");
|
|
293
|
|
294 return retval;
|
|
295 }
|
|
296
|
|
297 static gpointer
|
|
298 _gaim_desktop_item_copy (gpointer boxed)
|
|
299 {
|
|
300 return gaim_desktop_item_copy (boxed);
|
|
301 }
|
|
302
|
|
303 static void
|
|
304 _gaim_desktop_item_free (gpointer boxed)
|
|
305 {
|
|
306 gaim_desktop_item_unref (boxed);
|
|
307 }
|
|
308
|
|
309 /* Note, does not include the trailing \n */
|
|
310 static char *
|
|
311 my_fgets (char *buf, gsize bufsize, FILE *df)
|
|
312 {
|
|
313 int c;
|
|
314 gsize pos;
|
|
315
|
|
316 g_return_val_if_fail (buf != NULL, NULL);
|
|
317 g_return_val_if_fail (df != NULL, NULL);
|
|
318
|
|
319 pos = 0;
|
|
320 buf[0] = '\0';
|
|
321
|
|
322 do {
|
|
323 c = getc (df);
|
|
324 if (c == EOF || c == '\n')
|
|
325 break;
|
|
326 buf[pos++] = c;
|
|
327 } while (pos < bufsize-1);
|
|
328
|
|
329 if (c == EOF && pos == 0)
|
|
330 return NULL;
|
|
331
|
|
332 buf[pos++] = '\0';
|
|
333
|
|
334 return buf;
|
|
335 }
|
|
336
|
|
337 static Encoding
|
|
338 get_encoding (FILE *df)
|
|
339 {
|
|
340 gboolean old_kde = FALSE;
|
|
341 char buf [BUFSIZ];
|
|
342 gboolean all_valid_utf8 = TRUE;
|
|
343
|
|
344 while (my_fgets (buf, sizeof (buf), df) != NULL) {
|
|
345 if (strncmp (GAIM_DESKTOP_ITEM_ENCODING,
|
|
346 buf,
|
|
347 strlen (GAIM_DESKTOP_ITEM_ENCODING)) == 0) {
|
|
348 char *p = &buf[strlen (GAIM_DESKTOP_ITEM_ENCODING)];
|
|
349 if (*p == ' ')
|
|
350 p++;
|
|
351 if (*p != '=')
|
|
352 continue;
|
|
353 p++;
|
|
354 if (*p == ' ')
|
|
355 p++;
|
|
356 if (strcmp (p, "UTF-8") == 0) {
|
|
357 return ENCODING_UTF8;
|
|
358 } else if (strcmp (p, "Legacy-Mixed") == 0) {
|
|
359 return ENCODING_LEGACY_MIXED;
|
|
360 } else {
|
|
361 /* According to the spec we're not supposed
|
|
362 * to read a file like this */
|
|
363 return ENCODING_UNKNOWN;
|
|
364 }
|
|
365 } else if (strcmp ("[KDE Desktop Entry]", buf) == 0) {
|
|
366 old_kde = TRUE;
|
|
367 /* don't break yet, we still want to support
|
|
368 * Encoding even here */
|
|
369 }
|
|
370 if (all_valid_utf8 && ! g_utf8_validate (buf, -1, NULL))
|
|
371 all_valid_utf8 = FALSE;
|
|
372 }
|
|
373
|
|
374 if (old_kde)
|
|
375 return ENCODING_LEGACY_MIXED;
|
|
376
|
|
377 /* A dilemma, new KDE files are in UTF-8 but have no Encoding
|
|
378 * info, at this time we really can't tell. The best thing to
|
|
379 * do right now is to just assume UTF-8 if the whole file
|
|
380 * validates as utf8 I suppose */
|
|
381
|
|
382 if (all_valid_utf8)
|
|
383 return ENCODING_UTF8;
|
|
384 else
|
|
385 return ENCODING_LEGACY_MIXED;
|
|
386 }
|
|
387
|
|
388 static char *
|
|
389 snarf_locale_from_key (const char *key)
|
|
390 {
|
|
391 const char *brace;
|
|
392 char *locale, *p;
|
|
393
|
|
394 brace = strchr (key, '[');
|
|
395 if (brace == NULL)
|
|
396 return NULL;
|
|
397
|
|
398 locale = g_strdup (brace + 1);
|
|
399 if (*locale == '\0') {
|
|
400 g_free (locale);
|
|
401 return NULL;
|
|
402 }
|
|
403 p = strchr (locale, ']');
|
|
404 if (p == NULL) {
|
|
405 g_free (locale);
|
|
406 return NULL;
|
|
407 }
|
|
408 *p = '\0';
|
|
409 return locale;
|
|
410 }
|
|
411
|
|
412 static gboolean
|
|
413 check_locale (const char *locale)
|
|
414 {
|
|
415 GIConv cd = g_iconv_open ("UTF-8", locale);
|
|
416 if ((GIConv)-1 == cd)
|
|
417 return FALSE;
|
|
418 g_iconv_close (cd);
|
|
419 return TRUE;
|
|
420 }
|
|
421
|
|
422 static void
|
|
423 insert_locales (GHashTable *encodings, char *enc, ...)
|
|
424 {
|
|
425 va_list args;
|
|
426 char *s;
|
|
427
|
|
428 va_start (args, enc);
|
|
429 for (;;) {
|
|
430 s = va_arg (args, char *);
|
|
431 if (s == NULL)
|
|
432 break;
|
|
433 g_hash_table_insert (encodings, s, enc);
|
|
434 }
|
|
435 va_end (args);
|
|
436 }
|
|
437
|
|
438 /* make a standard conversion table from the desktop standard spec */
|
|
439 static GHashTable *
|
|
440 init_encodings (void)
|
|
441 {
|
|
442 GHashTable *encodings = g_hash_table_new (g_str_hash, g_str_equal);
|
|
443
|
|
444 /* "C" is plain ascii */
|
|
445 insert_locales (encodings, "ASCII", "C", NULL);
|
|
446
|
|
447 insert_locales (encodings, "ARMSCII-8", "by", NULL);
|
|
448 insert_locales (encodings, "BIG5", "zh_TW", NULL);
|
|
449 insert_locales (encodings, "CP1251", "be", "bg", NULL);
|
|
450 if (check_locale ("EUC-CN")) {
|
|
451 insert_locales (encodings, "EUC-CN", "zh_CN", NULL);
|
|
452 } else {
|
|
453 insert_locales (encodings, "GB2312", "zh_CN", NULL);
|
|
454 }
|
|
455 insert_locales (encodings, "EUC-JP", "ja", NULL);
|
|
456 insert_locales (encodings, "EUC-KR", "ko", NULL);
|
|
457 /*insert_locales (encodings, "GEORGIAN-ACADEMY", NULL);*/
|
|
458 insert_locales (encodings, "GEORGIAN-PS", "ka", NULL);
|
|
459 insert_locales (encodings, "ISO-8859-1", "br", "ca", "da", "de", "en", "es", "eu", "fi", "fr", "gl", "it", "nl", "wa", "no", "pt", "pt", "sv", NULL);
|
|
460 insert_locales (encodings, "ISO-8859-2", "cs", "hr", "hu", "pl", "ro", "sk", "sl", "sq", "sr", NULL);
|
|
461 insert_locales (encodings, "ISO-8859-3", "eo", NULL);
|
|
462 insert_locales (encodings, "ISO-8859-5", "mk", "sp", NULL);
|
|
463 insert_locales (encodings, "ISO-8859-7", "el", NULL);
|
|
464 insert_locales (encodings, "ISO-8859-9", "tr", NULL);
|
|
465 insert_locales (encodings, "ISO-8859-13", "lt", "lv", "mi", NULL);
|
|
466 insert_locales (encodings, "ISO-8859-14", "ga", "cy", NULL);
|
|
467 insert_locales (encodings, "ISO-8859-15", "et", NULL);
|
|
468 insert_locales (encodings, "KOI8-R", "ru", NULL);
|
|
469 insert_locales (encodings, "KOI8-U", "uk", NULL);
|
|
470 if (check_locale ("TCVN-5712")) {
|
|
471 insert_locales (encodings, "TCVN-5712", "vi", NULL);
|
|
472 } else {
|
|
473 insert_locales (encodings, "TCVN", "vi", NULL);
|
|
474 }
|
|
475 insert_locales (encodings, "TIS-620", "th", NULL);
|
|
476 /*insert_locales (encodings, "VISCII", NULL);*/
|
|
477
|
|
478 return encodings;
|
|
479 }
|
|
480
|
|
481 static const char *
|
|
482 get_encoding_from_locale (const char *locale)
|
|
483 {
|
|
484 char lang[3];
|
|
485 const char *encoding;
|
|
486 static GHashTable *encodings = NULL;
|
|
487
|
|
488 if (locale == NULL)
|
|
489 return NULL;
|
|
490
|
|
491 /* if locale includes encoding, use it */
|
|
492 encoding = strchr (locale, '.');
|
|
493 if (encoding != NULL) {
|
|
494 return encoding+1;
|
|
495 }
|
|
496
|
|
497 if (encodings == NULL)
|
|
498 encodings = init_encodings ();
|
|
499
|
|
500 /* first try the entire locale (at this point ll_CC) */
|
|
501 encoding = g_hash_table_lookup (encodings, locale);
|
|
502 if (encoding != NULL)
|
|
503 return encoding;
|
|
504
|
|
505 /* Try just the language */
|
|
506 strncpy (lang, locale, 2);
|
|
507 lang[2] = '\0';
|
|
508 return g_hash_table_lookup (encodings, lang);
|
|
509 }
|
|
510
|
|
511 static char *
|
|
512 decode_string_and_dup (const char *s)
|
|
513 {
|
|
514 char *p = g_malloc (strlen (s) + 1);
|
|
515 char *q = p;
|
|
516
|
|
517 do {
|
|
518 if (*s == '\\'){
|
|
519 switch (*(++s)){
|
|
520 case 's':
|
|
521 *p++ = ' ';
|
|
522 break;
|
|
523 case 't':
|
|
524 *p++ = '\t';
|
|
525 break;
|
|
526 case 'n':
|
|
527 *p++ = '\n';
|
|
528 break;
|
|
529 case '\\':
|
|
530 *p++ = '\\';
|
|
531 break;
|
|
532 case 'r':
|
|
533 *p++ = '\r';
|
|
534 break;
|
|
535 default:
|
|
536 *p++ = '\\';
|
|
537 *p++ = *s;
|
|
538 break;
|
|
539 }
|
|
540 } else {
|
|
541 *p++ = *s;
|
|
542 }
|
|
543 } while (*s++);
|
|
544
|
|
545 return q;
|
|
546 }
|
|
547
|
|
548 static char *
|
|
549 decode_string (const char *value, Encoding encoding, const char *locale)
|
|
550 {
|
|
551 char *retval = NULL;
|
|
552
|
|
553 /* if legacy mixed, then convert */
|
|
554 if (locale != NULL && encoding == ENCODING_LEGACY_MIXED) {
|
|
555 const char *char_encoding = get_encoding_from_locale (locale);
|
|
556 char *utf8_string;
|
|
557 if (char_encoding == NULL)
|
|
558 return NULL;
|
|
559 if (strcmp (char_encoding, "ASCII") == 0) {
|
|
560 return decode_string_and_dup (value);
|
|
561 }
|
|
562 utf8_string = g_convert (value, -1, "UTF-8", char_encoding,
|
|
563 NULL, NULL, NULL);
|
|
564 if (utf8_string == NULL)
|
|
565 return NULL;
|
|
566 retval = decode_string_and_dup (utf8_string);
|
|
567 g_free (utf8_string);
|
|
568 return retval;
|
|
569 /* if utf8, then validate */
|
|
570 } else if (locale != NULL && encoding == ENCODING_UTF8) {
|
|
571 if ( ! g_utf8_validate (value, -1, NULL))
|
|
572 /* invalid utf8, ignore this key */
|
|
573 return NULL;
|
|
574 return decode_string_and_dup (value);
|
|
575 } else {
|
|
576 /* Meaning this is not a localized string */
|
|
577 return decode_string_and_dup (value);
|
|
578 }
|
|
579 }
|
|
580
|
|
581 /************************************************************
|
|
582 * Parser: *
|
|
583 ************************************************************/
|
|
584
|
|
585 static gboolean G_GNUC_CONST
|
|
586 standard_is_boolean (const char * key)
|
|
587 {
|
|
588 static GHashTable *bools = NULL;
|
|
589
|
|
590 if (bools == NULL) {
|
|
591 bools = g_hash_table_new (g_str_hash, g_str_equal);
|
|
592 g_hash_table_insert (bools,
|
|
593 GAIM_DESKTOP_ITEM_NO_DISPLAY,
|
|
594 GAIM_DESKTOP_ITEM_NO_DISPLAY);
|
|
595 g_hash_table_insert (bools,
|
|
596 GAIM_DESKTOP_ITEM_HIDDEN,
|
|
597 GAIM_DESKTOP_ITEM_HIDDEN);
|
|
598 g_hash_table_insert (bools,
|
|
599 GAIM_DESKTOP_ITEM_TERMINAL,
|
|
600 GAIM_DESKTOP_ITEM_TERMINAL);
|
|
601 g_hash_table_insert (bools,
|
|
602 GAIM_DESKTOP_ITEM_READ_ONLY,
|
|
603 GAIM_DESKTOP_ITEM_READ_ONLY);
|
|
604 }
|
|
605
|
|
606 return g_hash_table_lookup (bools, key) != NULL;
|
|
607 }
|
|
608
|
|
609 static gboolean G_GNUC_CONST
|
|
610 standard_is_strings (const char *key)
|
|
611 {
|
|
612 static GHashTable *strings = NULL;
|
|
613
|
|
614 if (strings == NULL) {
|
|
615 strings = g_hash_table_new (g_str_hash, g_str_equal);
|
|
616 g_hash_table_insert (strings,
|
|
617 GAIM_DESKTOP_ITEM_FILE_PATTERN,
|
|
618 GAIM_DESKTOP_ITEM_FILE_PATTERN);
|
|
619 g_hash_table_insert (strings,
|
|
620 GAIM_DESKTOP_ITEM_ACTIONS,
|
|
621 GAIM_DESKTOP_ITEM_ACTIONS);
|
|
622 g_hash_table_insert (strings,
|
|
623 GAIM_DESKTOP_ITEM_MIME_TYPE,
|
|
624 GAIM_DESKTOP_ITEM_MIME_TYPE);
|
|
625 g_hash_table_insert (strings,
|
|
626 GAIM_DESKTOP_ITEM_PATTERNS,
|
|
627 GAIM_DESKTOP_ITEM_PATTERNS);
|
|
628 g_hash_table_insert (strings,
|
|
629 GAIM_DESKTOP_ITEM_SORT_ORDER,
|
|
630 GAIM_DESKTOP_ITEM_SORT_ORDER);
|
|
631 }
|
|
632
|
|
633 return g_hash_table_lookup (strings, key) != NULL;
|
|
634 }
|
|
635
|
|
636 /* If no need to cannonize, returns NULL */
|
|
637 static char *
|
|
638 cannonize (const char *key, const char *value)
|
|
639 {
|
|
640 if (standard_is_boolean (key)) {
|
|
641 if (value[0] == 'T' ||
|
|
642 value[0] == 't' ||
|
|
643 value[0] == 'Y' ||
|
|
644 value[0] == 'y' ||
|
|
645 atoi (value) != 0) {
|
|
646 return g_strdup ("true");
|
|
647 } else {
|
|
648 return g_strdup ("false");
|
|
649 }
|
|
650 } else if (standard_is_strings (key)) {
|
|
651 int len = strlen (value);
|
|
652 if (len == 0 || value[len-1] != ';') {
|
|
653 return g_strconcat (value, ";", NULL);
|
|
654 }
|
|
655 }
|
|
656 /* XXX: Perhaps we should canonize numeric values as well, but this
|
|
657 * has caused some subtle problems before so it needs to be done
|
|
658 * carefully if at all */
|
|
659 return NULL;
|
|
660 }
|
|
661
|
|
662 static void
|
|
663 insert_key (GaimDesktopItem *item,
|
|
664 Section *cur_section,
|
|
665 Encoding encoding,
|
|
666 const char *key,
|
|
667 const char *value,
|
|
668 gboolean old_kde,
|
|
669 gboolean no_translations)
|
|
670 {
|
|
671 char *k;
|
|
672 char *val;
|
|
673 /* we always store everything in UTF-8 */
|
|
674 if (cur_section == NULL &&
|
|
675 strcmp (key, GAIM_DESKTOP_ITEM_ENCODING) == 0) {
|
|
676 k = g_strdup (key);
|
|
677 val = g_strdup ("UTF-8");
|
|
678 } else {
|
|
679 char *locale = snarf_locale_from_key (key);
|
|
680 /* If we're ignoring translations */
|
|
681 if (no_translations && locale != NULL) {
|
|
682 g_free (locale);
|
|
683 return;
|
|
684 }
|
|
685 val = decode_string (value, encoding, locale);
|
|
686
|
|
687 /* Ignore this key, it's whacked */
|
|
688 if (val == NULL) {
|
|
689 g_free (locale);
|
|
690 return;
|
|
691 }
|
|
692
|
|
693 g_strchomp (val);
|
|
694
|
|
695 /* For old KDE entries, we can also split by a comma
|
|
696 * on sort order, so convert to semicolons */
|
|
697 if (old_kde &&
|
|
698 cur_section == NULL &&
|
|
699 strcmp (key, GAIM_DESKTOP_ITEM_SORT_ORDER) == 0 &&
|
|
700 strchr (val, ';') == NULL) {
|
|
701 int i;
|
|
702 for (i = 0; val[i] != '\0'; i++) {
|
|
703 if (val[i] == ',')
|
|
704 val[i] = ';';
|
|
705 }
|
|
706 }
|
|
707
|
|
708 /* Check some types, not perfect, but catches a lot
|
|
709 * of things */
|
|
710 if (cur_section == NULL) {
|
|
711 char *cannon = cannonize (key, val);
|
|
712 if (cannon != NULL) {
|
|
713 g_free (val);
|
|
714 val = cannon;
|
|
715 }
|
|
716 }
|
|
717
|
|
718 k = g_strdup (key);
|
|
719
|
|
720 /* Take care of the language part */
|
|
721 if (locale != NULL &&
|
|
722 strcmp (locale, "C") == 0) {
|
|
723 char *p;
|
|
724 /* Whack C locale */
|
|
725 p = strchr (k, '[');
|
|
726 if(p) *p = '\0';
|
|
727 g_free (locale);
|
|
728 } else if (locale != NULL) {
|
|
729 char *p, *brace;
|
|
730
|
|
731 /* Whack the encoding part */
|
|
732 p = strchr (locale, '.');
|
|
733 if (p != NULL)
|
|
734 *p = '\0';
|
|
735
|
|
736 if (g_list_find_custom (item->languages, locale,
|
|
737 (GCompareFunc)strcmp) == NULL) {
|
|
738 item->languages = g_list_prepend
|
|
739 (item->languages, locale);
|
|
740 } else {
|
|
741 g_free (locale);
|
|
742 }
|
|
743
|
|
744 /* Whack encoding from encoding in the key */
|
|
745 brace = strchr (k, '[');
|
|
746 if(brace != NULL) {
|
|
747 p = strchr (brace, '.');
|
|
748 if (p != NULL) {
|
|
749 *p = ']';
|
|
750 *(p+1) = '\0';
|
|
751 }
|
|
752 }
|
|
753 }
|
|
754 }
|
|
755
|
|
756
|
|
757 if (cur_section == NULL) {
|
|
758 /* only add to list if we haven't seen it before */
|
|
759 if (g_hash_table_lookup (item->main_hash, k) == NULL) {
|
|
760 item->keys = g_list_prepend (item->keys,
|
|
761 g_strdup (k));
|
|
762 }
|
|
763 /* later duplicates override earlier ones */
|
|
764 g_hash_table_replace (item->main_hash, k, val);
|
|
765 } else {
|
|
766 char *full = g_strdup_printf
|
|
767 ("%s/%s",
|
|
768 cur_section->name, k);
|
|
769 /* only add to list if we haven't seen it before */
|
|
770 if (g_hash_table_lookup (item->main_hash, full) == NULL) {
|
|
771 cur_section->keys =
|
|
772 g_list_prepend (cur_section->keys, k);
|
|
773 }
|
|
774 /* later duplicates override earlier ones */
|
|
775 g_hash_table_replace (item->main_hash,
|
|
776 full, val);
|
|
777 }
|
|
778 }
|
|
779
|
|
780 static const char *
|
|
781 lookup (const GaimDesktopItem *item, const char *key)
|
|
782 {
|
|
783 return g_hash_table_lookup (item->main_hash, key);
|
|
784 }
|
|
785
|
|
786 static void
|
|
787 setup_type (GaimDesktopItem *item, const char *uri)
|
|
788 {
|
|
789 const char *type = g_hash_table_lookup (item->main_hash,
|
|
790 GAIM_DESKTOP_ITEM_TYPE);
|
|
791 if (type == NULL && uri != NULL) {
|
|
792 char *base = g_path_get_basename (uri);
|
|
793 if (base != NULL &&
|
|
794 strcmp (base, ".directory") == 0) {
|
|
795 /* This gotta be a directory */
|
|
796 g_hash_table_replace (item->main_hash,
|
|
797 g_strdup (GAIM_DESKTOP_ITEM_TYPE),
|
|
798 g_strdup ("Directory"));
|
|
799 item->keys = g_list_prepend
|
|
800 (item->keys, g_strdup (GAIM_DESKTOP_ITEM_TYPE));
|
|
801 item->type = GAIM_DESKTOP_ITEM_TYPE_DIRECTORY;
|
|
802 } else {
|
|
803 item->type = GAIM_DESKTOP_ITEM_TYPE_NULL;
|
|
804 }
|
|
805 g_free (base);
|
|
806 } else {
|
|
807 item->type = type_from_string (type);
|
|
808 }
|
|
809 }
|
|
810
|
|
811 static const char *
|
|
812 lookup_locale (const GaimDesktopItem *item, const char *key, const char *locale)
|
|
813 {
|
|
814 if (locale == NULL ||
|
|
815 strcmp (locale, "C") == 0) {
|
|
816 return lookup (item, key);
|
|
817 } else {
|
|
818 const char *ret;
|
|
819 char *full = g_strdup_printf ("%s[%s]", key, locale);
|
|
820 ret = lookup (item, full);
|
|
821 g_free (full);
|
|
822 return ret;
|
|
823 }
|
|
824 }
|
|
825
|
|
826 /* fallback to find something suitable for C locale */
|
|
827 static char *
|
|
828 try_english_key (GaimDesktopItem *item, const char *key)
|
|
829 {
|
|
830 char *str;
|
|
831 char *locales[] = { "en_US", "en_GB", "en_AU", "en", NULL };
|
|
832 int i;
|
|
833
|
|
834 str = NULL;
|
|
835 for (i = 0; locales[i] != NULL && str == NULL; i++) {
|
|
836 str = g_strdup (lookup_locale (item, key, locales[i]));
|
|
837 }
|
|
838 if (str != NULL) {
|
|
839 /* We need a 7-bit ascii string, so whack all
|
|
840 * above 127 chars */
|
|
841 guchar *p;
|
|
842 for (p = (guchar *)str; *p != '\0'; p++) {
|
|
843 if (*p > 127)
|
|
844 *p = '?';
|
|
845 }
|
|
846 }
|
|
847 return str;
|
|
848 }
|
|
849
|
|
850
|
|
851 static void
|
|
852 sanitize (GaimDesktopItem *item, const char *uri)
|
|
853 {
|
|
854 const char *type;
|
|
855
|
|
856 type = lookup (item, GAIM_DESKTOP_ITEM_TYPE);
|
|
857
|
|
858 /* understand old gnome style url exec thingies */
|
|
859 if (type != NULL && strcmp (type, "URL") == 0) {
|
|
860 const char *exec = lookup (item, GAIM_DESKTOP_ITEM_EXEC);
|
|
861 set (item, GAIM_DESKTOP_ITEM_TYPE, "Link");
|
|
862 if (exec != NULL) {
|
|
863 /* Note, this must be in this order */
|
|
864 set (item, GAIM_DESKTOP_ITEM_URL, exec);
|
|
865 set (item, GAIM_DESKTOP_ITEM_EXEC, NULL);
|
|
866 }
|
|
867 }
|
|
868
|
|
869 /* we make sure we have Name, Encoding and Version */
|
|
870 if (lookup (item, GAIM_DESKTOP_ITEM_NAME) == NULL) {
|
|
871 char *name = try_english_key (item, GAIM_DESKTOP_ITEM_NAME);
|
|
872 /* If no name, use the basename */
|
|
873 if (name == NULL && uri != NULL)
|
|
874 name = g_path_get_basename (uri);
|
|
875 /* If no uri either, use same default as gnome_desktop_item_new */
|
|
876 if (name == NULL)
|
|
877 name = g_strdup (_("No name"));
|
|
878 g_hash_table_replace (item->main_hash,
|
|
879 g_strdup (GAIM_DESKTOP_ITEM_NAME),
|
|
880 name);
|
|
881 item->keys = g_list_prepend
|
|
882 (item->keys, g_strdup (GAIM_DESKTOP_ITEM_NAME));
|
|
883 }
|
|
884 if (lookup (item, GAIM_DESKTOP_ITEM_ENCODING) == NULL) {
|
|
885 /* We store everything in UTF-8 so write that down */
|
|
886 g_hash_table_replace (item->main_hash,
|
|
887 g_strdup (GAIM_DESKTOP_ITEM_ENCODING),
|
|
888 g_strdup ("UTF-8"));
|
|
889 item->keys = g_list_prepend
|
|
890 (item->keys, g_strdup (GAIM_DESKTOP_ITEM_ENCODING));
|
|
891 }
|
|
892 if (lookup (item, GAIM_DESKTOP_ITEM_VERSION) == NULL) {
|
|
893 /* this is the version that we follow, so write it down */
|
|
894 g_hash_table_replace (item->main_hash,
|
|
895 g_strdup (GAIM_DESKTOP_ITEM_VERSION),
|
|
896 g_strdup ("1.0"));
|
|
897 item->keys = g_list_prepend
|
|
898 (item->keys, g_strdup (GAIM_DESKTOP_ITEM_VERSION));
|
|
899 }
|
|
900 }
|
|
901
|
|
902 enum {
|
|
903 FirstBrace,
|
|
904 OnSecHeader,
|
|
905 IgnoreToEOL,
|
|
906 IgnoreToEOLFirst,
|
|
907 KeyDef,
|
|
908 KeyDefOnKey,
|
|
909 KeyValue
|
|
910 };
|
|
911
|
|
912 static GaimDesktopItem *
|
|
913 ditem_load (FILE *df,
|
|
914 gboolean no_translations,
|
|
915 const char *uri)
|
|
916 {
|
|
917 int state;
|
|
918 char CharBuffer [1024];
|
|
919 char *next = CharBuffer;
|
|
920 int c;
|
|
921 Encoding encoding;
|
|
922 GaimDesktopItem *item;
|
|
923 Section *cur_section = NULL;
|
|
924 char *key = NULL;
|
|
925 gboolean old_kde = FALSE;
|
|
926
|
|
927 encoding = get_encoding (df);
|
|
928 if (encoding == ENCODING_UNKNOWN) {
|
|
929 fclose(df);
|
|
930 /* spec says, don't read this file */
|
|
931 printf ("Unknown encoding of .desktop file");
|
|
932
|
|
933 return NULL;
|
|
934 }
|
|
935
|
|
936 /* Rewind since get_encoding goes through the file */
|
|
937 if (fseek(df, 0L, SEEK_SET)) {
|
|
938 fclose(df);
|
|
939 /* spec says, don't read this file */
|
|
940 printf ("fseek() error on .desktop file");
|
|
941 return NULL;
|
|
942 }
|
|
943
|
|
944 item = _gaim_desktop_item_new ();
|
|
945 item->modified = FALSE;
|
|
946
|
|
947 /* Note: location and mtime are filled in by the new_from_file
|
|
948 * function since it has those values */
|
|
949
|
|
950 #define GAIM_DESKTOP_ITEM_OVERFLOW (next == &CharBuffer [sizeof(CharBuffer)-1])
|
|
951
|
|
952 state = FirstBrace;
|
|
953 while ((c = getc (df)) != EOF) {
|
|
954 if (c == '\r') /* Ignore Carriage Return */
|
|
955 continue;
|
|
956
|
|
957 switch (state) {
|
|
958
|
|
959 case OnSecHeader:
|
|
960 if (c == ']' || GAIM_DESKTOP_ITEM_OVERFLOW) {
|
|
961 *next = '\0';
|
|
962 next = CharBuffer;
|
|
963
|
|
964 /* keys were inserted in reverse */
|
|
965 if (cur_section != NULL &&
|
|
966 cur_section->keys != NULL) {
|
|
967 cur_section->keys = g_list_reverse
|
|
968 (cur_section->keys);
|
|
969 }
|
|
970 if (strcmp (CharBuffer,
|
|
971 "KDE Desktop Entry") == 0) {
|
|
972 /* Main section */
|
|
973 cur_section = NULL;
|
|
974 old_kde = TRUE;
|
|
975 } else if (strcmp (CharBuffer,
|
|
976 "Desktop Entry") == 0) {
|
|
977 /* Main section */
|
|
978 cur_section = NULL;
|
|
979 } else {
|
|
980 cur_section = g_new0 (Section, 1);
|
|
981 cur_section->name =
|
|
982 g_strdup (CharBuffer);
|
|
983 cur_section->keys = NULL;
|
|
984 item->sections = g_list_prepend
|
|
985 (item->sections, cur_section);
|
|
986 }
|
|
987 state = IgnoreToEOL;
|
|
988 } else if (c == '[') {
|
|
989 /* FIXME: probably error out instead of ignoring this */
|
|
990 } else {
|
|
991 *next++ = c;
|
|
992 }
|
|
993 break;
|
|
994
|
|
995 case IgnoreToEOL:
|
|
996 case IgnoreToEOLFirst:
|
|
997 if (c == '\n'){
|
|
998 if (state == IgnoreToEOLFirst)
|
|
999 state = FirstBrace;
|
|
1000 else
|
|
1001 state = KeyDef;
|
|
1002 next = CharBuffer;
|
|
1003 }
|
|
1004 break;
|
|
1005
|
|
1006 case FirstBrace:
|
|
1007 case KeyDef:
|
|
1008 case KeyDefOnKey:
|
|
1009 if (c == '#') {
|
|
1010 if (state == FirstBrace)
|
|
1011 state = IgnoreToEOLFirst;
|
|
1012 else
|
|
1013 state = IgnoreToEOL;
|
|
1014 break;
|
|
1015 }
|
|
1016
|
|
1017 if (c == '[' && state != KeyDefOnKey){
|
|
1018 state = OnSecHeader;
|
|
1019 next = CharBuffer;
|
|
1020 g_free (key);
|
|
1021 key = NULL;
|
|
1022 break;
|
|
1023 }
|
|
1024 /* On first pass, don't allow dangling keys */
|
|
1025 if (state == FirstBrace)
|
|
1026 break;
|
|
1027
|
|
1028 if ((c == ' ' && state != KeyDefOnKey) || c == '\t')
|
|
1029 break;
|
|
1030
|
|
1031 if (c == '\n' || GAIM_DESKTOP_ITEM_OVERFLOW) { /* Abort Definition */
|
|
1032 next = CharBuffer;
|
|
1033 state = KeyDef;
|
|
1034 break;
|
|
1035 }
|
|
1036
|
|
1037 if (c == '=' || GAIM_DESKTOP_ITEM_OVERFLOW){
|
|
1038 *next = '\0';
|
|
1039
|
|
1040 g_free (key);
|
|
1041 key = g_strdup (CharBuffer);
|
|
1042 state = KeyValue;
|
|
1043 next = CharBuffer;
|
|
1044 } else {
|
|
1045 *next++ = c;
|
|
1046 state = KeyDefOnKey;
|
|
1047 }
|
|
1048 break;
|
|
1049
|
|
1050 case KeyValue:
|
|
1051 if (GAIM_DESKTOP_ITEM_OVERFLOW || c == '\n'){
|
|
1052 *next = '\0';
|
|
1053
|
|
1054 insert_key (item, cur_section, encoding,
|
|
1055 key, CharBuffer, old_kde,
|
|
1056 no_translations);
|
|
1057
|
|
1058 g_free (key);
|
|
1059 key = NULL;
|
|
1060
|
|
1061 state = (c == '\n') ? KeyDef : IgnoreToEOL;
|
|
1062 next = CharBuffer;
|
|
1063 } else {
|
|
1064 *next++ = c;
|
|
1065 }
|
|
1066 break;
|
|
1067
|
|
1068 } /* switch */
|
|
1069
|
|
1070 } /* while ((c = getc_unlocked (f)) != EOF) */
|
|
1071 if (c == EOF && state == KeyValue) {
|
|
1072 *next = '\0';
|
|
1073
|
|
1074 insert_key (item, cur_section, encoding,
|
|
1075 key, CharBuffer, old_kde,
|
|
1076 no_translations);
|
|
1077
|
|
1078 g_free (key);
|
|
1079 key = NULL;
|
|
1080 }
|
|
1081
|
|
1082 #undef GAIM_DESKTOP_ITEM_OVERFLOW
|
|
1083
|
|
1084 /* keys were inserted in reverse */
|
|
1085 if (cur_section != NULL &&
|
|
1086 cur_section->keys != NULL) {
|
|
1087 cur_section->keys = g_list_reverse (cur_section->keys);
|
|
1088 }
|
|
1089 /* keys were inserted in reverse */
|
|
1090 item->keys = g_list_reverse (item->keys);
|
|
1091 /* sections were inserted in reverse */
|
|
1092 item->sections = g_list_reverse (item->sections);
|
|
1093
|
|
1094 /* sanitize some things */
|
|
1095 sanitize (item, uri);
|
|
1096
|
|
1097 /* make sure that we set up the type */
|
|
1098 setup_type (item, uri);
|
|
1099
|
|
1100 fclose (df);
|
|
1101
|
|
1102 return item;
|
|
1103 }
|
|
1104
|
|
1105 static void
|
|
1106 copy_string_hash (gpointer key, gpointer value, gpointer user_data)
|
|
1107 {
|
|
1108 GHashTable *copy = user_data;
|
|
1109 g_hash_table_replace (copy,
|
|
1110 g_strdup (key),
|
|
1111 g_strdup (value));
|
|
1112 }
|
|
1113
|
|
1114 static void
|
|
1115 free_section (gpointer data, gpointer user_data)
|
|
1116 {
|
|
1117 Section *section = data;
|
|
1118
|
|
1119 g_free (section->name);
|
|
1120 section->name = NULL;
|
|
1121
|
|
1122 g_list_foreach (section->keys, (GFunc)g_free, NULL);
|
|
1123 g_list_free (section->keys);
|
|
1124 section->keys = NULL;
|
|
1125
|
|
1126 g_free (section);
|
|
1127 }
|
|
1128
|
|
1129 static Section *
|
|
1130 dup_section (Section *sec)
|
|
1131 {
|
|
1132 GList *li;
|
|
1133 Section *retval = g_new0 (Section, 1);
|
|
1134
|
|
1135 retval->name = g_strdup (sec->name);
|
|
1136
|
|
1137 retval->keys = g_list_copy (sec->keys);
|
|
1138 for (li = retval->keys; li != NULL; li = li->next)
|
|
1139 li->data = g_strdup (li->data);
|
|
1140
|
|
1141 return retval;
|
|
1142 }
|
|
1143
|
|
1144 /**************************************************************************
|
|
1145 * Public functions
|
|
1146 **************************************************************************/
|
|
1147 GaimDesktopItem *
|
|
1148 gaim_desktop_item_new_from_file (const char *filename)
|
|
1149 {
|
|
1150 GaimDesktopItem *retval;
|
|
1151 FILE *dfile;
|
|
1152
|
|
1153 g_return_val_if_fail (filename != NULL, NULL);
|
|
1154
|
|
1155 dfile = g_fopen(filename, "r");
|
|
1156 if (!dfile) {
|
|
1157 printf ("Can't open %s: %s", filename, strerror(errno));
|
|
1158 return NULL;
|
|
1159 }
|
|
1160
|
|
1161 retval = ditem_load(dfile, FALSE, filename);
|
|
1162
|
|
1163 return retval;
|
|
1164 }
|
|
1165
|
|
1166 GaimDesktopItemType
|
|
1167 gaim_desktop_item_get_entry_type (const GaimDesktopItem *item)
|
|
1168 {
|
|
1169 g_return_val_if_fail (item != NULL, 0);
|
|
1170 g_return_val_if_fail (item->refcount > 0, 0);
|
|
1171
|
|
1172 return item->type;
|
|
1173 }
|
|
1174
|
|
1175 const char *
|
|
1176 gaim_desktop_item_get_string (const GaimDesktopItem *item,
|
|
1177 const char *attr)
|
|
1178 {
|
|
1179 g_return_val_if_fail (item != NULL, NULL);
|
|
1180 g_return_val_if_fail (item->refcount > 0, NULL);
|
|
1181 g_return_val_if_fail (attr != NULL, NULL);
|
|
1182
|
|
1183 return lookup (item, attr);
|
|
1184 }
|
|
1185
|
|
1186 GaimDesktopItem *
|
|
1187 gaim_desktop_item_copy (const GaimDesktopItem *item)
|
|
1188 {
|
|
1189 GList *li;
|
|
1190 GaimDesktopItem *retval;
|
|
1191
|
|
1192 g_return_val_if_fail (item != NULL, NULL);
|
|
1193 g_return_val_if_fail (item->refcount > 0, NULL);
|
|
1194
|
|
1195 retval = _gaim_desktop_item_new ();
|
|
1196
|
|
1197 retval->type = item->type;
|
|
1198 retval->modified = item->modified;
|
|
1199 retval->location = g_strdup (item->location);
|
|
1200 retval->mtime = item->mtime;
|
|
1201
|
|
1202 /* Languages */
|
|
1203 retval->languages = g_list_copy (item->languages);
|
|
1204 for (li = retval->languages; li != NULL; li = li->next)
|
|
1205 li->data = g_strdup (li->data);
|
|
1206
|
|
1207 /* Keys */
|
|
1208 retval->keys = g_list_copy (item->keys);
|
|
1209 for (li = retval->keys; li != NULL; li = li->next)
|
|
1210 li->data = g_strdup (li->data);
|
|
1211
|
|
1212 /* Sections */
|
|
1213 retval->sections = g_list_copy (item->sections);
|
|
1214 for (li = retval->sections; li != NULL; li = li->next)
|
|
1215 li->data = dup_section (li->data);
|
|
1216
|
|
1217 retval->main_hash = g_hash_table_new_full (g_str_hash, g_str_equal,
|
|
1218 (GDestroyNotify) g_free,
|
|
1219 (GDestroyNotify) g_free);
|
|
1220
|
|
1221 g_hash_table_foreach (item->main_hash,
|
|
1222 copy_string_hash,
|
|
1223 retval->main_hash);
|
|
1224
|
|
1225 return retval;
|
|
1226 }
|
|
1227
|
|
1228 void
|
|
1229 gaim_desktop_item_unref (GaimDesktopItem *item)
|
|
1230 {
|
|
1231 g_return_if_fail (item != NULL);
|
|
1232 g_return_if_fail (item->refcount > 0);
|
|
1233
|
|
1234 item->refcount--;
|
|
1235
|
|
1236 if(item->refcount != 0)
|
|
1237 return;
|
|
1238
|
|
1239 g_list_foreach (item->languages, (GFunc)g_free, NULL);
|
|
1240 g_list_free (item->languages);
|
|
1241 item->languages = NULL;
|
|
1242
|
|
1243 g_list_foreach (item->keys, (GFunc)g_free, NULL);
|
|
1244 g_list_free (item->keys);
|
|
1245 item->keys = NULL;
|
|
1246
|
|
1247 g_list_foreach (item->sections, free_section, NULL);
|
|
1248 g_list_free (item->sections);
|
|
1249 item->sections = NULL;
|
|
1250
|
|
1251 g_hash_table_destroy (item->main_hash);
|
|
1252 item->main_hash = NULL;
|
|
1253
|
|
1254 g_free (item->location);
|
|
1255 item->location = NULL;
|
|
1256
|
|
1257 g_free (item);
|
|
1258 }
|
|
1259
|
|
1260 GType
|
|
1261 gaim_desktop_item_get_type (void)
|
|
1262 {
|
|
1263 static GType type = 0;
|
|
1264
|
|
1265 if (type == 0) {
|
|
1266 type = g_boxed_type_register_static ("GaimDesktopItem",
|
|
1267 _gaim_desktop_item_copy,
|
|
1268 _gaim_desktop_item_free);
|
|
1269 }
|
|
1270
|
|
1271 return type;
|
|
1272 }
|