Doh. Upon further investigation I think these translations were made
[pidgin-git.git] / libpurple / desktopitem.c
blobd7f6d39d2d7e59ab72146b926ad7c212e027a668
1 /**
2 * @file purple-desktop-item.c Functions for managing .desktop files
3 * @ingroup core
4 */
6 /* Purple is the legal property of its developers, whose names are too numerous
7 * to list here. Please refer to the COPYRIGHT file distributed with this
8 * source distribution.
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
27 * The following code has been adapted from gnome-desktop-item.[ch],
28 * as found on gnome-desktop-2.8.1.
30 * Copyright (C) 2004 by Alceste Scalas <alceste.scalas@gmx.net>.
32 * Original copyright notice:
34 * Copyright (C) 1999, 2000 Red Hat Inc.
35 * Copyright (C) 2001 Sid Vicious
36 * All rights reserved.
38 * This file is part of the Gnome Library.
40 * The Gnome Library is free software; you can redistribute it and/or
41 * modify it under the terms of the GNU Library General Public License as
42 * published by the Free Software Foundation; either version 2 of the
43 * License, or (at your option) any later version.
45 * The Gnome Library is distributed in the hope that it will be useful,
46 * but WITHOUT ANY WARRANTY; without even the implied warranty of
47 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
48 * Library General Public License for more details.
50 * You should have received a copy of the GNU Library General Public
51 * License along with the Gnome Library; see the file COPYING.LIB. If not,
52 * write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
53 * Boston, MA 02111-1301, USA.
56 #include "internal.h"
57 #include <errno.h>
58 #include <stdio.h>
59 #include <string.h>
60 #include <time.h>
61 #include "desktopitem.h"
63 struct _PurpleDesktopItem {
64 int refcount;
66 /* all languages used */
67 GList *languages;
69 PurpleDesktopItemType type;
71 /* `modified' means that the ditem has been
72 * modified since the last save. */
73 gboolean modified;
75 /* Keys of the main section only */
76 GList *keys;
78 GList *sections;
80 /* This includes ALL keys, including
81 * other sections, separated by '/' */
82 GHashTable *main_hash;
84 char *location;
86 time_t mtime;
89 typedef struct {
90 char *name;
91 GList *keys;
92 } Section;
94 typedef enum {
95 ENCODING_UNKNOWN,
96 ENCODING_UTF8,
97 ENCODING_LEGACY_MIXED
98 } Encoding;
100 /**************************************************************************
101 * Private utility functions
102 **************************************************************************/
103 static PurpleDesktopItemType
104 type_from_string (const char *type)
106 if (!type)
107 return PURPLE_DESKTOP_ITEM_TYPE_NULL;
109 switch (type [0]) {
110 case 'A':
111 if (purple_strequal (type, "Application"))
112 return PURPLE_DESKTOP_ITEM_TYPE_APPLICATION;
113 break;
114 case 'L':
115 if (purple_strequal (type, "Link"))
116 return PURPLE_DESKTOP_ITEM_TYPE_LINK;
117 break;
118 case 'F':
119 if (purple_strequal (type, "FSDevice"))
120 return PURPLE_DESKTOP_ITEM_TYPE_FSDEVICE;
121 break;
122 case 'M':
123 if (purple_strequal (type, "MimeType"))
124 return PURPLE_DESKTOP_ITEM_TYPE_MIME_TYPE;
125 break;
126 case 'D':
127 if (purple_strequal (type, "Directory"))
128 return PURPLE_DESKTOP_ITEM_TYPE_DIRECTORY;
129 break;
130 case 'S':
131 if (purple_strequal (type, "Service"))
132 return PURPLE_DESKTOP_ITEM_TYPE_SERVICE;
134 else if (purple_strequal (type, "ServiceType"))
135 return PURPLE_DESKTOP_ITEM_TYPE_SERVICE_TYPE;
136 break;
137 default:
138 break;
141 return PURPLE_DESKTOP_ITEM_TYPE_OTHER;
144 static Section *
145 find_section (PurpleDesktopItem *item, const char *section)
147 GList *li;
148 Section *sec;
150 if (section == NULL)
151 return NULL;
152 if (purple_strequal (section, "Desktop Entry"))
153 return NULL;
155 for (li = item->sections; li != NULL; li = li->next) {
156 sec = li->data;
157 if (purple_strequal (sec->name, section))
158 return sec;
161 sec = g_new0 (Section, 1);
162 sec->name = g_strdup (section);
163 sec->keys = NULL;
165 item->sections = g_list_append (item->sections, sec);
167 /* Don't mark the item modified, this is just an empty section,
168 * it won't be saved even */
170 return sec;
173 static Section *
174 section_from_key (PurpleDesktopItem *item, const char *key)
176 char *p;
177 char *name;
178 Section *sec;
180 if (key == NULL)
181 return NULL;
183 p = strchr (key, '/');
184 if (p == NULL)
185 return NULL;
187 name = g_strndup (key, p - key);
189 sec = find_section (item, name);
191 g_free (name);
193 return sec;
196 static const char *
197 key_basename (const char *key)
199 char *p = strrchr (key, '/');
200 if (p != NULL)
201 return p+1;
202 else
203 return key;
206 static void
207 set (PurpleDesktopItem *item, const char *key, const char *value)
209 Section *sec = section_from_key (item, key);
211 if (sec != NULL) {
212 if (value != NULL) {
213 if (g_hash_table_lookup (item->main_hash, key) == NULL)
214 sec->keys = g_list_append
215 (sec->keys,
216 g_strdup (key_basename (key)));
218 g_hash_table_replace (item->main_hash,
219 g_strdup (key),
220 g_strdup (value));
221 } else {
222 GList *list = g_list_find_custom
223 (sec->keys, key_basename (key),
224 (GCompareFunc)strcmp);
225 if (list != NULL) {
226 g_free (list->data);
227 sec->keys =
228 g_list_delete_link (sec->keys, list);
230 g_hash_table_remove (item->main_hash, key);
232 } else {
233 if (value != NULL) {
234 if (g_hash_table_lookup (item->main_hash, key) == NULL)
235 item->keys = g_list_append (item->keys,
236 g_strdup (key));
238 g_hash_table_replace (item->main_hash,
239 g_strdup (key),
240 g_strdup (value));
241 } else {
242 GList *list = g_list_find_custom
243 (item->keys, key, (GCompareFunc)strcmp);
244 if (list != NULL) {
245 g_free (list->data);
246 item->keys =
247 g_list_delete_link (item->keys, list);
249 g_hash_table_remove (item->main_hash, key);
252 item->modified = TRUE;
256 static void
257 _purple_desktop_item_set_string (PurpleDesktopItem *item,
258 const char *attr,
259 const char *value)
261 g_return_if_fail (item != NULL);
262 g_return_if_fail (item->refcount > 0);
263 g_return_if_fail (attr != NULL);
265 set (item, attr, value);
267 if (purple_strequal (attr, PURPLE_DESKTOP_ITEM_TYPE))
268 item->type = type_from_string (value);
271 static PurpleDesktopItem *
272 _purple_desktop_item_new (void)
274 PurpleDesktopItem *retval;
276 retval = g_new0 (PurpleDesktopItem, 1);
278 retval->refcount++;
280 retval->main_hash = g_hash_table_new_full (g_str_hash, g_str_equal,
281 (GDestroyNotify) g_free,
282 (GDestroyNotify) g_free);
284 /* These are guaranteed to be set */
285 _purple_desktop_item_set_string (retval,
286 PURPLE_DESKTOP_ITEM_NAME,
287 _("No name"));
288 _purple_desktop_item_set_string (retval,
289 PURPLE_DESKTOP_ITEM_ENCODING,
290 "UTF-8");
291 _purple_desktop_item_set_string (retval,
292 PURPLE_DESKTOP_ITEM_VERSION,
293 "1.0");
295 return retval;
298 static gpointer
299 _purple_desktop_item_copy (gpointer boxed)
301 return purple_desktop_item_copy (boxed);
304 static void
305 _purple_desktop_item_free (gpointer boxed)
307 purple_desktop_item_unref (boxed);
310 /* Note, does not include the trailing \n */
311 static char *
312 my_fgets (char *buf, gsize bufsize, FILE *df)
314 int c;
315 gsize pos;
317 g_return_val_if_fail (buf != NULL, NULL);
318 g_return_val_if_fail (df != NULL, NULL);
320 pos = 0;
321 buf[0] = '\0';
323 do {
324 c = getc (df);
325 if (c == EOF || c == '\n')
326 break;
327 buf[pos++] = c;
328 } while (pos < bufsize-1);
330 if (c == EOF && pos == 0)
331 return NULL;
333 buf[pos] = '\0';
335 return buf;
338 static Encoding
339 get_encoding (FILE *df)
341 gboolean old_kde = FALSE;
342 char buf [BUFSIZ];
343 gboolean all_valid_utf8 = TRUE;
345 while (my_fgets (buf, sizeof (buf), df) != NULL) {
346 if (strncmp (PURPLE_DESKTOP_ITEM_ENCODING,
347 buf,
348 strlen (PURPLE_DESKTOP_ITEM_ENCODING)) == 0) {
349 char *p = &buf[strlen (PURPLE_DESKTOP_ITEM_ENCODING)];
350 if (*p == ' ')
351 p++;
352 if (*p != '=')
353 continue;
354 p++;
355 if (*p == ' ')
356 p++;
357 if (purple_strequal (p, "UTF-8")) {
358 return ENCODING_UTF8;
359 } else if (purple_strequal (p, "Legacy-Mixed")) {
360 return ENCODING_LEGACY_MIXED;
361 } else {
362 /* According to the spec we're not supposed
363 * to read a file like this */
364 return ENCODING_UNKNOWN;
366 } else if (purple_strequal ("[KDE Desktop Entry]", buf)) {
367 old_kde = TRUE;
368 /* don't break yet, we still want to support
369 * Encoding even here */
371 if (all_valid_utf8 && ! g_utf8_validate (buf, -1, NULL))
372 all_valid_utf8 = FALSE;
375 if (old_kde)
376 return ENCODING_LEGACY_MIXED;
378 /* A dilemma, new KDE files are in UTF-8 but have no Encoding
379 * info, at this time we really can't tell. The best thing to
380 * do right now is to just assume UTF-8 if the whole file
381 * validates as utf8 I suppose */
383 if (all_valid_utf8)
384 return ENCODING_UTF8;
385 else
386 return ENCODING_LEGACY_MIXED;
389 static char *
390 snarf_locale_from_key (const char *key)
392 const char *brace;
393 char *locale, *p;
395 brace = strchr (key, '[');
396 if (brace == NULL)
397 return NULL;
399 locale = g_strdup (brace + 1);
400 if (*locale == '\0') {
401 g_free (locale);
402 return NULL;
404 p = strchr (locale, ']');
405 if (p == NULL) {
406 g_free (locale);
407 return NULL;
409 *p = '\0';
410 return locale;
413 static gboolean
414 check_locale (const char *locale)
416 GIConv cd = g_iconv_open ("UTF-8", locale);
417 if ((GIConv)-1 == cd)
418 return FALSE;
419 g_iconv_close (cd);
420 return TRUE;
423 static void
424 insert_locales (GHashTable *encodings, char *enc, ...)
426 va_list args;
427 char *s;
429 va_start (args, enc);
430 for (;;) {
431 s = va_arg (args, char *);
432 if (s == NULL)
433 break;
434 g_hash_table_insert (encodings, s, enc);
436 va_end (args);
439 /* make a standard conversion table from the desktop standard spec */
440 static GHashTable *
441 init_encodings (void)
443 GHashTable *encodings = g_hash_table_new (g_str_hash, g_str_equal);
445 /* "C" is plain ascii */
446 insert_locales (encodings, "ASCII", "C", NULL);
448 insert_locales (encodings, "ARMSCII-8", "by", NULL);
449 insert_locales (encodings, "BIG5", "zh_TW", NULL);
450 insert_locales (encodings, "CP1251", "be", "bg", NULL);
451 if (check_locale ("EUC-CN")) {
452 insert_locales (encodings, "EUC-CN", "zh_CN", NULL);
453 } else {
454 insert_locales (encodings, "GB2312", "zh_CN", NULL);
456 insert_locales (encodings, "EUC-JP", "ja", NULL);
457 insert_locales (encodings, "EUC-KR", "ko", NULL);
458 /*insert_locales (encodings, "GEORGIAN-ACADEMY", NULL);*/
459 insert_locales (encodings, "GEORGIAN-PS", "ka", NULL);
460 insert_locales (encodings, "ISO-8859-1", "br", "ca", "da", "de", "en", "es", "eu", "fi", "fr", "gl", "it", "nl", "wa", "no", "pt", "pt", "sv", NULL);
461 insert_locales (encodings, "ISO-8859-2", "cs", "hr", "hu", "pl", "ro", "sk", "sl", "sq", "sr", NULL);
462 insert_locales (encodings, "ISO-8859-3", "eo", NULL);
463 insert_locales (encodings, "ISO-8859-5", "mk", "sp", NULL);
464 insert_locales (encodings, "ISO-8859-7", "el", NULL);
465 insert_locales (encodings, "ISO-8859-9", "tr", NULL);
466 insert_locales (encodings, "ISO-8859-13", "lt", "lv", "mi", NULL);
467 insert_locales (encodings, "ISO-8859-14", "ga", "cy", NULL);
468 insert_locales (encodings, "ISO-8859-15", "et", NULL);
469 insert_locales (encodings, "KOI8-R", "ru", NULL);
470 insert_locales (encodings, "KOI8-U", "uk", NULL);
471 if (check_locale ("TCVN-5712")) {
472 insert_locales (encodings, "TCVN-5712", "vi", NULL);
473 } else {
474 insert_locales (encodings, "TCVN", "vi", NULL);
476 insert_locales (encodings, "TIS-620", "th", NULL);
477 /*insert_locales (encodings, "VISCII", NULL);*/
479 return encodings;
482 static const char *
483 get_encoding_from_locale (const char *locale)
485 char lang[3];
486 const char *encoding;
487 static GHashTable *encodings = NULL;
489 if (locale == NULL)
490 return NULL;
492 /* if locale includes encoding, use it */
493 encoding = strchr (locale, '.');
494 if (encoding != NULL) {
495 return encoding+1;
498 if (encodings == NULL)
499 encodings = init_encodings ();
501 /* first try the entire locale (at this point ll_CC) */
502 encoding = g_hash_table_lookup (encodings, locale);
503 if (encoding != NULL)
504 return encoding;
506 /* Try just the language */
507 strncpy (lang, locale, 2);
508 lang[2] = '\0';
509 return g_hash_table_lookup (encodings, lang);
512 static char *
513 decode_string_and_dup (const char *s)
515 char *p = g_malloc (strlen (s) + 1);
516 char *q = p;
518 do {
519 if (*s == '\\'){
520 switch (*(++s)){
521 case 's':
522 *p++ = ' ';
523 break;
524 case 't':
525 *p++ = '\t';
526 break;
527 case 'n':
528 *p++ = '\n';
529 break;
530 case '\\':
531 *p++ = '\\';
532 break;
533 case 'r':
534 *p++ = '\r';
535 break;
536 default:
537 *p++ = '\\';
538 *p++ = *s;
539 break;
541 } else {
542 *p++ = *s;
544 } while (*s++);
546 return q;
549 static char *
550 decode_string (const char *value, Encoding encoding, const char *locale)
552 char *retval = NULL;
554 /* if legacy mixed, then convert */
555 if (locale != NULL && encoding == ENCODING_LEGACY_MIXED) {
556 const char *char_encoding = get_encoding_from_locale (locale);
557 char *utf8_string;
558 if (char_encoding == NULL)
559 return NULL;
560 if (purple_strequal (char_encoding, "ASCII")) {
561 return decode_string_and_dup (value);
563 utf8_string = g_convert (value, -1, "UTF-8", char_encoding,
564 NULL, NULL, NULL);
565 if (utf8_string == NULL)
566 return NULL;
567 retval = decode_string_and_dup (utf8_string);
568 g_free (utf8_string);
569 return retval;
570 /* if utf8, then validate */
571 } else if (locale != NULL && encoding == ENCODING_UTF8) {
572 if ( ! g_utf8_validate (value, -1, NULL))
573 /* invalid utf8, ignore this key */
574 return NULL;
575 return decode_string_and_dup (value);
576 } else {
577 /* Meaning this is not a localized string */
578 return decode_string_and_dup (value);
582 /************************************************************
583 * Parser: *
584 ************************************************************/
586 static gboolean G_GNUC_CONST
587 standard_is_boolean (const char * key)
589 static GHashTable *bools = NULL;
591 if (bools == NULL) {
592 bools = g_hash_table_new (g_str_hash, g_str_equal);
593 g_hash_table_insert (bools,
594 PURPLE_DESKTOP_ITEM_NO_DISPLAY,
595 PURPLE_DESKTOP_ITEM_NO_DISPLAY);
596 g_hash_table_insert (bools,
597 PURPLE_DESKTOP_ITEM_HIDDEN,
598 PURPLE_DESKTOP_ITEM_HIDDEN);
599 g_hash_table_insert (bools,
600 PURPLE_DESKTOP_ITEM_TERMINAL,
601 PURPLE_DESKTOP_ITEM_TERMINAL);
602 g_hash_table_insert (bools,
603 PURPLE_DESKTOP_ITEM_READ_ONLY,
604 PURPLE_DESKTOP_ITEM_READ_ONLY);
607 return g_hash_table_lookup (bools, key) != NULL;
610 static gboolean G_GNUC_CONST
611 standard_is_strings (const char *key)
613 static GHashTable *strings = NULL;
615 if (strings == NULL) {
616 strings = g_hash_table_new (g_str_hash, g_str_equal);
617 g_hash_table_insert (strings,
618 PURPLE_DESKTOP_ITEM_FILE_PATTERN,
619 PURPLE_DESKTOP_ITEM_FILE_PATTERN);
620 g_hash_table_insert (strings,
621 PURPLE_DESKTOP_ITEM_ACTIONS,
622 PURPLE_DESKTOP_ITEM_ACTIONS);
623 g_hash_table_insert (strings,
624 PURPLE_DESKTOP_ITEM_MIME_TYPE,
625 PURPLE_DESKTOP_ITEM_MIME_TYPE);
626 g_hash_table_insert (strings,
627 PURPLE_DESKTOP_ITEM_PATTERNS,
628 PURPLE_DESKTOP_ITEM_PATTERNS);
629 g_hash_table_insert (strings,
630 PURPLE_DESKTOP_ITEM_SORT_ORDER,
631 PURPLE_DESKTOP_ITEM_SORT_ORDER);
634 return g_hash_table_lookup (strings, key) != NULL;
637 /* If no need to cannonize, returns NULL */
638 static char *
639 cannonize (const char *key, const char *value)
641 if (standard_is_boolean (key)) {
642 if (value[0] == 'T' ||
643 value[0] == 't' ||
644 value[0] == 'Y' ||
645 value[0] == 'y' ||
646 atoi (value) != 0) {
647 return g_strdup ("true");
648 } else {
649 return g_strdup ("false");
651 } else if (standard_is_strings (key)) {
652 int len = strlen (value);
653 if (len == 0 || value[len-1] != ';') {
654 return g_strconcat (value, ";", NULL);
657 /* XXX: Perhaps we should canonize numeric values as well, but this
658 * has caused some subtle problems before so it needs to be done
659 * carefully if at all */
660 return NULL;
663 static void
664 insert_key (PurpleDesktopItem *item,
665 Section *cur_section,
666 Encoding encoding,
667 const char *key,
668 const char *value,
669 gboolean old_kde,
670 gboolean no_translations)
672 char *k;
673 char *val;
674 /* we always store everything in UTF-8 */
675 if (cur_section == NULL &&
676 purple_strequal (key, PURPLE_DESKTOP_ITEM_ENCODING)) {
677 k = g_strdup (key);
678 val = g_strdup ("UTF-8");
679 } else {
680 char *locale = snarf_locale_from_key (key);
681 /* If we're ignoring translations */
682 if (no_translations && locale != NULL) {
683 g_free (locale);
684 return;
686 val = decode_string (value, encoding, locale);
688 /* Ignore this key, it's whacked */
689 if (val == NULL) {
690 g_free (locale);
691 return;
694 g_strchomp (val);
696 /* For old KDE entries, we can also split by a comma
697 * on sort order, so convert to semicolons */
698 if (old_kde &&
699 cur_section == NULL &&
700 purple_strequal (key, PURPLE_DESKTOP_ITEM_SORT_ORDER) &&
701 strchr (val, ';') == NULL) {
702 int i;
703 for (i = 0; val[i] != '\0'; i++) {
704 if (val[i] == ',')
705 val[i] = ';';
709 /* Check some types, not perfect, but catches a lot
710 * of things */
711 if (cur_section == NULL) {
712 char *cannon = cannonize (key, val);
713 if (cannon != NULL) {
714 g_free (val);
715 val = cannon;
719 k = g_strdup (key);
721 /* Take care of the language part */
722 if (locale != NULL &&
723 purple_strequal (locale, "C")) {
724 char *p;
725 /* Whack C locale */
726 p = strchr (k, '[');
727 if(p) *p = '\0';
728 g_free (locale);
729 } else if (locale != NULL) {
730 char *p, *brace;
732 /* Whack the encoding part */
733 p = strchr (locale, '.');
734 if (p != NULL)
735 *p = '\0';
737 if (g_list_find_custom (item->languages, locale,
738 (GCompareFunc)strcmp) == NULL) {
739 item->languages = g_list_prepend
740 (item->languages, locale);
741 } else {
742 g_free (locale);
745 /* Whack encoding from encoding in the key */
746 brace = strchr (k, '[');
747 if(brace != NULL) {
748 p = strchr (brace, '.');
749 if (p != NULL) {
750 *p = ']';
751 *(p+1) = '\0';
758 if (cur_section == NULL) {
759 /* only add to list if we haven't seen it before */
760 if (g_hash_table_lookup (item->main_hash, k) == NULL) {
761 item->keys = g_list_prepend (item->keys,
762 g_strdup (k));
764 /* later duplicates override earlier ones */
765 g_hash_table_replace (item->main_hash, k, val);
766 } else {
767 char *full = g_strdup_printf
768 ("%s/%s",
769 cur_section->name, k);
770 /* only add to list if we haven't seen it before */
771 if (g_hash_table_lookup (item->main_hash, full) == NULL) {
772 cur_section->keys =
773 g_list_prepend (cur_section->keys, k);
775 /* later duplicates override earlier ones */
776 g_hash_table_replace (item->main_hash,
777 full, val);
781 static const char *
782 lookup (const PurpleDesktopItem *item, const char *key)
784 return g_hash_table_lookup (item->main_hash, key);
787 static void
788 setup_type (PurpleDesktopItem *item, const char *uri)
790 const char *type = g_hash_table_lookup (item->main_hash,
791 PURPLE_DESKTOP_ITEM_TYPE);
792 if (type == NULL && uri != NULL) {
793 char *base = g_path_get_basename (uri);
794 if (purple_strequal(base, ".directory")) {
795 /* This gotta be a directory */
796 g_hash_table_replace (item->main_hash,
797 g_strdup (PURPLE_DESKTOP_ITEM_TYPE),
798 g_strdup ("Directory"));
799 item->keys = g_list_prepend
800 (item->keys, g_strdup (PURPLE_DESKTOP_ITEM_TYPE));
801 item->type = PURPLE_DESKTOP_ITEM_TYPE_DIRECTORY;
802 } else {
803 item->type = PURPLE_DESKTOP_ITEM_TYPE_NULL;
805 g_free (base);
806 } else {
807 item->type = type_from_string (type);
811 static const char *
812 lookup_locale (const PurpleDesktopItem *item, const char *key, const char *locale)
814 if (locale == NULL ||
815 purple_strequal (locale, "C")) {
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;
827 * Fallback to find something suitable for C locale.
829 * @return A newly allocated string which should be g_freed by the caller.
831 static char *
832 try_english_key (PurpleDesktopItem *item, const char *key)
834 char *str = NULL;
835 char *locales[] = { "en_US", "en_GB", "en_AU", "en", NULL };
836 int i;
838 for (i = 0; locales[i] != NULL && str == NULL; i++) {
839 str = g_strdup (lookup_locale (item, key, locales[i]));
841 if (str != NULL) {
842 /* We need a 7-bit ascii string, so whack all
843 * above 127 chars */
844 guchar *p;
845 for (p = (guchar *)str; *p != '\0'; p++) {
846 if (*p > 127)
847 *p = '?';
850 return str;
854 static void
855 sanitize (PurpleDesktopItem *item, const char *uri)
857 const char *type;
859 type = lookup (item, PURPLE_DESKTOP_ITEM_TYPE);
861 /* understand old gnome style url exec thingies */
862 if (purple_strequal(type, "URL")) {
863 const char *exec = lookup (item, PURPLE_DESKTOP_ITEM_EXEC);
864 set (item, PURPLE_DESKTOP_ITEM_TYPE, "Link");
865 if (exec != NULL) {
866 /* Note, this must be in this order */
867 set (item, PURPLE_DESKTOP_ITEM_URL, exec);
868 set (item, PURPLE_DESKTOP_ITEM_EXEC, NULL);
872 /* we make sure we have Name, Encoding and Version */
873 if (lookup (item, PURPLE_DESKTOP_ITEM_NAME) == NULL) {
874 char *name = try_english_key (item, PURPLE_DESKTOP_ITEM_NAME);
875 /* If no name, use the basename */
876 if (name == NULL && uri != NULL)
877 name = g_path_get_basename (uri);
878 /* If no uri either, use same default as gnome_desktop_item_new */
879 if (name == NULL)
880 name = g_strdup (_("No name"));
881 g_hash_table_replace (item->main_hash,
882 g_strdup (PURPLE_DESKTOP_ITEM_NAME),
883 name);
884 item->keys = g_list_prepend
885 (item->keys, g_strdup (PURPLE_DESKTOP_ITEM_NAME));
887 if (lookup (item, PURPLE_DESKTOP_ITEM_ENCODING) == NULL) {
888 /* We store everything in UTF-8 so write that down */
889 g_hash_table_replace (item->main_hash,
890 g_strdup (PURPLE_DESKTOP_ITEM_ENCODING),
891 g_strdup ("UTF-8"));
892 item->keys = g_list_prepend
893 (item->keys, g_strdup (PURPLE_DESKTOP_ITEM_ENCODING));
895 if (lookup (item, PURPLE_DESKTOP_ITEM_VERSION) == NULL) {
896 /* this is the version that we follow, so write it down */
897 g_hash_table_replace (item->main_hash,
898 g_strdup (PURPLE_DESKTOP_ITEM_VERSION),
899 g_strdup ("1.0"));
900 item->keys = g_list_prepend
901 (item->keys, g_strdup (PURPLE_DESKTOP_ITEM_VERSION));
905 enum {
906 FirstBrace,
907 OnSecHeader,
908 IgnoreToEOL,
909 IgnoreToEOLFirst,
910 KeyDef,
911 KeyDefOnKey,
912 KeyValue
915 static PurpleDesktopItem *
916 ditem_load (FILE *df,
917 gboolean no_translations,
918 const char *uri)
920 int state;
921 char CharBuffer [1024];
922 char *next = CharBuffer;
923 int c;
924 Encoding encoding;
925 PurpleDesktopItem *item;
926 Section *cur_section = NULL;
927 char *key = NULL;
928 gboolean old_kde = FALSE;
930 encoding = get_encoding (df);
931 if (encoding == ENCODING_UNKNOWN) {
932 fclose(df);
933 /* spec says, don't read this file */
934 printf ("Unknown encoding of .desktop file");
936 return NULL;
939 /* Rewind since get_encoding goes through the file */
940 if (fseek(df, 0L, SEEK_SET)) {
941 fclose(df);
942 /* spec says, don't read this file */
943 printf ("fseek() error on .desktop file");
944 return NULL;
947 item = _purple_desktop_item_new ();
948 item->modified = FALSE;
950 /* Note: location and mtime are filled in by the new_from_file
951 * function since it has those values */
953 #define PURPLE_DESKTOP_ITEM_OVERFLOW (next == &CharBuffer [sizeof(CharBuffer)-1])
955 state = FirstBrace;
956 while ((c = getc (df)) != EOF) {
957 if (c == '\r') /* Ignore Carriage Return */
958 continue;
960 switch (state) {
962 case OnSecHeader:
963 if (c == ']' || PURPLE_DESKTOP_ITEM_OVERFLOW) {
964 *next = '\0';
965 next = CharBuffer;
967 /* keys were inserted in reverse */
968 if (cur_section != NULL &&
969 cur_section->keys != NULL) {
970 cur_section->keys = g_list_reverse
971 (cur_section->keys);
973 if (purple_strequal (CharBuffer, "KDE Desktop Entry")) {
974 /* Main section */
975 cur_section = NULL;
976 old_kde = TRUE;
977 } else if (purple_strequal(CharBuffer, "Desktop Entry")) {
978 /* Main section */
979 cur_section = NULL;
980 } else {
981 cur_section = g_new0 (Section, 1);
982 cur_section->name =
983 g_strdup (CharBuffer);
984 cur_section->keys = NULL;
985 item->sections = g_list_prepend
986 (item->sections, cur_section);
988 state = IgnoreToEOL;
989 } else if (c == '[') {
990 /* FIXME: probably error out instead of ignoring this */
991 } else {
992 *next++ = c;
994 break;
996 case IgnoreToEOL:
997 case IgnoreToEOLFirst:
998 if (c == '\n'){
999 if (state == IgnoreToEOLFirst)
1000 state = FirstBrace;
1001 else
1002 state = KeyDef;
1003 next = CharBuffer;
1005 break;
1007 case FirstBrace:
1008 case KeyDef:
1009 case KeyDefOnKey:
1010 if (c == '#') {
1011 if (state == FirstBrace)
1012 state = IgnoreToEOLFirst;
1013 else
1014 state = IgnoreToEOL;
1015 break;
1018 if (c == '[' && state != KeyDefOnKey){
1019 state = OnSecHeader;
1020 next = CharBuffer;
1021 g_free (key);
1022 key = NULL;
1023 break;
1025 /* On first pass, don't allow dangling keys */
1026 if (state == FirstBrace)
1027 break;
1029 if ((c == ' ' && state != KeyDefOnKey) || c == '\t')
1030 break;
1032 if (c == '\n' || PURPLE_DESKTOP_ITEM_OVERFLOW) { /* Abort Definition */
1033 next = CharBuffer;
1034 state = KeyDef;
1035 break;
1038 if (c == '=' || PURPLE_DESKTOP_ITEM_OVERFLOW){
1039 *next = '\0';
1041 g_free (key);
1042 key = g_strdup (CharBuffer);
1043 state = KeyValue;
1044 next = CharBuffer;
1045 } else {
1046 *next++ = c;
1047 state = KeyDefOnKey;
1049 break;
1051 case KeyValue:
1052 if (PURPLE_DESKTOP_ITEM_OVERFLOW || c == '\n'){
1053 *next = '\0';
1055 insert_key (item, cur_section, encoding,
1056 key, CharBuffer, old_kde,
1057 no_translations);
1059 g_free (key);
1060 key = NULL;
1062 state = (c == '\n') ? KeyDef : IgnoreToEOL;
1063 next = CharBuffer;
1064 } else {
1065 *next++ = c;
1067 break;
1069 } /* switch */
1071 } /* while ((c = getc_unlocked (f)) != EOF) */
1072 if (c == EOF && state == KeyValue) {
1073 *next = '\0';
1075 insert_key (item, cur_section, encoding,
1076 key, CharBuffer, old_kde,
1077 no_translations);
1079 g_free (key);
1080 key = NULL;
1083 #undef PURPLE_DESKTOP_ITEM_OVERFLOW
1085 /* keys were inserted in reverse */
1086 if (cur_section != NULL &&
1087 cur_section->keys != NULL) {
1088 cur_section->keys = g_list_reverse (cur_section->keys);
1090 /* keys were inserted in reverse */
1091 item->keys = g_list_reverse (item->keys);
1092 /* sections were inserted in reverse */
1093 item->sections = g_list_reverse (item->sections);
1095 /* sanitize some things */
1096 sanitize (item, uri);
1098 /* make sure that we set up the type */
1099 setup_type (item, uri);
1101 fclose (df);
1103 return item;
1106 static void
1107 copy_string_hash (gpointer key, gpointer value, gpointer user_data)
1109 GHashTable *copy = user_data;
1110 g_hash_table_replace (copy,
1111 g_strdup (key),
1112 g_strdup (value));
1115 static void
1116 free_section (gpointer data, gpointer user_data)
1118 Section *section = data;
1120 g_free (section->name);
1121 section->name = NULL;
1123 g_list_foreach (section->keys, (GFunc)g_free, NULL);
1124 g_list_free (section->keys);
1125 section->keys = NULL;
1127 g_free (section);
1130 static Section *
1131 dup_section (Section *sec)
1133 GList *li;
1134 Section *retval = g_new0 (Section, 1);
1136 retval->name = g_strdup (sec->name);
1138 retval->keys = g_list_copy (sec->keys);
1139 for (li = retval->keys; li != NULL; li = li->next)
1140 li->data = g_strdup (li->data);
1142 return retval;
1145 /**************************************************************************
1146 * Public functions
1147 **************************************************************************/
1148 PurpleDesktopItem *
1149 purple_desktop_item_new_from_file (const char *filename)
1151 PurpleDesktopItem *retval;
1152 FILE *dfile;
1154 g_return_val_if_fail (filename != NULL, NULL);
1156 dfile = g_fopen(filename, "r");
1157 if (!dfile) {
1158 printf ("Can't open %s: %s", filename, g_strerror(errno));
1159 return NULL;
1162 retval = ditem_load(dfile, FALSE, filename);
1164 return retval;
1167 PurpleDesktopItemType
1168 purple_desktop_item_get_entry_type (const PurpleDesktopItem *item)
1170 g_return_val_if_fail (item != NULL, 0);
1171 g_return_val_if_fail (item->refcount > 0, 0);
1173 return item->type;
1176 const char *
1177 purple_desktop_item_get_string (const PurpleDesktopItem *item,
1178 const char *attr)
1180 g_return_val_if_fail (item != NULL, NULL);
1181 g_return_val_if_fail (item->refcount > 0, NULL);
1182 g_return_val_if_fail (attr != NULL, NULL);
1184 return lookup (item, attr);
1187 PurpleDesktopItem *
1188 purple_desktop_item_copy (const PurpleDesktopItem *item)
1190 GList *li;
1191 PurpleDesktopItem *retval;
1193 g_return_val_if_fail (item != NULL, NULL);
1194 g_return_val_if_fail (item->refcount > 0, NULL);
1196 retval = _purple_desktop_item_new ();
1198 retval->type = item->type;
1199 retval->modified = item->modified;
1200 retval->location = g_strdup (item->location);
1201 retval->mtime = item->mtime;
1203 /* Languages */
1204 retval->languages = g_list_copy (item->languages);
1205 for (li = retval->languages; li != NULL; li = li->next)
1206 li->data = g_strdup (li->data);
1208 /* Keys */
1209 retval->keys = g_list_copy (item->keys);
1210 for (li = retval->keys; li != NULL; li = li->next)
1211 li->data = g_strdup (li->data);
1213 /* Sections */
1214 retval->sections = g_list_copy (item->sections);
1215 for (li = retval->sections; li != NULL; li = li->next)
1216 li->data = dup_section (li->data);
1218 retval->main_hash = g_hash_table_new_full (g_str_hash, g_str_equal,
1219 (GDestroyNotify) g_free,
1220 (GDestroyNotify) g_free);
1222 g_hash_table_foreach (item->main_hash,
1223 copy_string_hash,
1224 retval->main_hash);
1226 return retval;
1229 void
1230 purple_desktop_item_unref (PurpleDesktopItem *item)
1232 g_return_if_fail (item != NULL);
1233 g_return_if_fail (item->refcount > 0);
1235 item->refcount--;
1237 if(item->refcount != 0)
1238 return;
1240 g_list_foreach (item->languages, (GFunc)g_free, NULL);
1241 g_list_free (item->languages);
1242 item->languages = NULL;
1244 g_list_foreach (item->keys, (GFunc)g_free, NULL);
1245 g_list_free (item->keys);
1246 item->keys = NULL;
1248 g_list_foreach (item->sections, free_section, NULL);
1249 g_list_free (item->sections);
1250 item->sections = NULL;
1252 g_hash_table_destroy (item->main_hash);
1253 item->main_hash = NULL;
1255 g_free (item->location);
1256 item->location = NULL;
1258 g_free (item);
1261 GType
1262 purple_desktop_item_get_type (void)
1264 static GType type = 0;
1266 if (type == 0) {
1267 type = g_boxed_type_register_static ("PurpleDesktopItem",
1268 _purple_desktop_item_copy,
1269 _purple_desktop_item_free);
1272 return type;