Bug Summary

File:libcafe-desktop/cafe-desktop-item.c
Warning:line 2027, column 2
Undefined or garbage value returned to caller

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name cafe-desktop-item.c -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=all -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/rootdir/libcafe-desktop -fcoverage-compilation-dir=/rootdir/libcafe-desktop -resource-dir /usr/lib/llvm-19/lib/clang/19 -D HAVE_CONFIG_H -I . -I .. -I /usr/include/gdk-pixbuf-2.0 -I /usr/include/glib-2.0 -I /usr/lib/x86_64-linux-gnu/glib-2.0/include -I /usr/include/sysprof-6 -I /usr/include/libpng16 -I /usr/include/x86_64-linux-gnu -I /usr/include/webp -I /usr/include/libmount -I /usr/include/blkid -I /usr/include/ctk-3.0 -I /usr/include/pango-1.0 -I /usr/include/harfbuzz -I /usr/include/freetype2 -I /usr/include/fribidi -I /usr/include/cairo -I /usr/include/pixman-1 -I /usr/include/gio-unix-2.0 -I /usr/include/atk-1.0 -I /usr/include/at-spi2-atk/2.0 -I /usr/include/at-spi-2.0 -I /usr/include/dbus-1.0 -I /usr/lib/x86_64-linux-gnu/dbus-1.0/include -I /usr/include/startup-notification-1.0 -I /usr/include/dconf -I /usr/include/glib-2.0 -I /usr/lib/x86_64-linux-gnu/glib-2.0/include -I /usr/include/sysprof-6 -I /usr/include/libmount -I /usr/include/blkid -D G_LOG_DOMAIN="CafeDesktop" -D CAFELOCALEDIR="/usr/share/locale" -D PNP_IDS="/usr/share/libcafe-desktop/pnp.ids" -D ISO_CODES_PREFIX="/usr" -D PIC -internal-isystem /usr/lib/llvm-19/lib/clang/19/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -ferror-limit 19 -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -analyzer-checker deadcode.DeadStores -analyzer-checker alpha.deadcode.UnreachableCode -analyzer-checker alpha.core.CastSize -analyzer-checker alpha.core.CastToStruct -analyzer-checker alpha.core.IdenticalExpr -analyzer-checker alpha.security.ArrayBoundV2 -analyzer-checker alpha.security.MallocOverflow -analyzer-checker alpha.security.ReturnPtrRange -analyzer-checker alpha.unix.SimpleStream -analyzer-checker alpha.unix.cstring.BufferOverlap -analyzer-checker alpha.unix.cstring.NotNullTerminated -analyzer-checker alpha.unix.cstring.OutOfBounds -analyzer-checker alpha.core.FixedAddr -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /rootdir/html-report/2024-12-27-190749-15074-1 -x c cafe-desktop-item.c
1/* -*- Mode: C; c-set-style: linux indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2/* cafe-desktop-item.c - CAFE Desktop File Representation
3
4 Copyright (C) 1999, 2000 Red Hat Inc.
5 Copyright (C) 2001 Sid Vicious
6 All rights reserved.
7
8 This file is part of the Cafe Library.
9
10 Developed by Elliot Lee <sopwith@redhat.com> and Sid Vicious
11
12 The Cafe Library is free software; you can redistribute it and/or
13 modify it under the terms of the GNU Library General Public License as
14 published by the Free Software Foundation; either version 2 of the
15 License, or (at your option) any later version.
16
17 The Cafe Library is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 Library General Public License for more details.
21
22 You should have received a copy of the GNU Library General Public
23 License along with the Cafe Library; see the file COPYING.LIB. If not,
24 write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
25 Boston, MA 02110-1301, USA. */
26/*
27 @NOTATION@
28 */
29
30#include "config.h"
31
32#include <limits.h>
33#include <ctype.h>
34#include <stdio.h>
35#include <glib.h>
36#include <sys/types.h>
37#include <dirent.h>
38#include <unistd.h>
39#include <time.h>
40#include <string.h>
41#include <glib/gi18n-lib.h>
42#include <locale.h>
43#include <stdlib.h>
44
45#include <gio/gio.h>
46
47#ifdef HAVE_STARTUP_NOTIFICATION
48#define SN_API_NOT_YET_FROZEN
49#include <libsn/sn.h>
50#include <cdk/cdk.h>
51#include <cdk/cdkx.h>
52#include <ctk/ctk.h>
53#endif
54
55#define sure_string(s)((s)!=((void*)0)?(s):"") ((s)!=NULL((void*)0)?(s):"")
56
57#define CAFE_DESKTOP_USE_UNSTABLE_API
58#undef CAFE_DISABLE_DEPRECATED
59#include <cafe-desktop-item.h>
60#include <cafe-desktop-utils.h>
61
62#include "private.h"
63
64struct _CafeDesktopItem {
65 int refcount;
66
67 /* all languages used */
68 GList *languages;
69
70 CafeDesktopItemType type;
71
72 /* `modified' means that the ditem has been
73 * modified since the last save. */
74 gboolean modified;
75
76 /* Keys of the main section only */
77 GList *keys;
78
79 GList *sections;
80
81 /* This includes ALL keys, including
82 * other sections, separated by '/' */
83 GHashTable *main_hash;
84
85 char *location;
86
87 time_t mtime;
88
89 guint32 launch_time;
90};
91
92/* If mtime is set to this, set_location won't update mtime,
93 * this is to be used internally only. */
94#define DONT_UPDATE_MTIME((time_t)-2) ((time_t)-2)
95
96typedef struct {
97 char *name;
98 GList *keys;
99} Section;
100
101typedef enum {
102 ENCODING_UNKNOWN,
103 ENCODING_UTF8,
104 ENCODING_LEGACY_MIXED
105} Encoding;
106
107/*
108 * IO reading utils, that look like the libc buffered io stuff
109 */
110
111#define READ_BUF_SIZE(32 * 1024) (32 * 1024)
112
113typedef struct {
114 GFile *file;
115 GFileInputStream *stream;
116 char *uri;
117 char *buf;
118 gboolean buf_needs_free;
119 gboolean past_first_read;
120 gboolean eof;
121 guint64 size;
122 gsize pos;
123} ReadBuf;
124
125static CafeDesktopItem *ditem_load (ReadBuf *rb,
126 gboolean no_translations,
127 GError **error);
128static gboolean ditem_save (CafeDesktopItem *item,
129 const char *uri,
130 GError **error);
131
132static void cafe_desktop_item_set_location_gfile (CafeDesktopItem *item,
133 GFile *file);
134
135static CafeDesktopItem *cafe_desktop_item_new_from_gfile (GFile *file,
136 CafeDesktopItemLoadFlags flags,
137 GError **error);
138
139static int
140readbuf_getc (ReadBuf *rb)
141{
142 if (rb->eof)
143 return EOF(-1);
144
145 if (rb->size == 0 ||
146 rb->pos == rb->size) {
147 gssize bytes_read;
148
149 if (rb->stream == NULL((void*)0))
150 bytes_read = 0;
151 else
152 bytes_read = g_input_stream_read (G_INPUT_STREAM (rb->stream)((((GInputStream*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((rb->stream)), ((g_input_stream_get_type ()))))))
,
153 rb->buf,
154 READ_BUF_SIZE(32 * 1024),
155 NULL((void*)0), NULL((void*)0));
156
157 /* FIXME: handle errors other than EOF */
158 if (bytes_read <= 0) {
159 rb->eof = TRUE(!(0));
160 return EOF(-1);
161 }
162
163 if (rb->size != 0)
164 rb->past_first_read = TRUE(!(0));
165 rb->size = bytes_read;
166 rb->pos = 0;
167
168 }
169
170 return (guchar) rb->buf[rb->pos++];
171}
172
173/* Note, does not include the trailing \n */
174static char *
175readbuf_gets (char *buf, gsize bufsize, ReadBuf *rb)
176{
177 int c;
178 gsize pos;
179
180 g_return_val_if_fail (buf != NULL, NULL)do { if ((buf != ((void*)0))) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "buf != NULL"); return
(((void*)0)); } } while (0)
;
181 g_return_val_if_fail (rb != NULL, NULL)do { if ((rb != ((void*)0))) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "rb != NULL"); return
(((void*)0)); } } while (0)
;
182
183 pos = 0;
184 buf[0] = '\0';
185
186 do {
187 c = readbuf_getc (rb);
188 if (c == EOF(-1) || c == '\n')
189 break;
190 buf[pos++] = c;
191 } while (pos < bufsize-1);
192
193 if (c == EOF(-1) && pos == 0)
194 return NULL((void*)0);
195
196 buf[pos++] = '\0';
197
198 return buf;
199}
200
201static ReadBuf *
202readbuf_open (GFile *file, GError **error)
203{
204 GError *local_error;
205 GFileInputStream *stream;
206 char *uri;
207 ReadBuf *rb;
208
209 g_return_val_if_fail (file != NULL, NULL)do { if ((file != ((void*)0))) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "file != NULL");
return (((void*)0)); } } while (0)
;
210
211 uri = g_file_get_uri (file);
212 local_error = NULL((void*)0);
213 stream = g_file_read (file, NULL((void*)0), &local_error);
214
215 if (stream == NULL((void*)0)) {
216 g_set_error (error,
217 /* FIXME: better errors */
218 CAFE_DESKTOP_ITEM_ERRORcafe_desktop_item_error_quark (),
219 CAFE_DESKTOP_ITEM_ERROR_CANNOT_OPEN,
220 _("Error reading file '%s': %s")((char *) g_dgettext ("cafe-desktop", "Error reading file '%s': %s"
))
,
221 uri, local_error->message);
222 g_error_free (local_error);
223 g_free (uri);
224 return NULL((void*)0);
225 }
226
227 rb = g_new0 (ReadBuf, 1)((ReadBuf *) g_malloc0_n ((1), sizeof (ReadBuf)));
228 rb->stream = stream;
229 rb->file = g_file_dup (file);
230 rb->uri = uri;
231 rb->buf = g_malloc (READ_BUF_SIZE(32 * 1024));
232 rb->buf_needs_free = TRUE(!(0));
233 /* rb->past_first_read = FALSE; */
234 /* rb->eof = FALSE; */
235 /* rb->size = 0; */
236 /* rb->pos = 0; */
237
238 return rb;
239}
240
241static ReadBuf *
242readbuf_new_from_string (const char *uri, const char *string, gssize length)
243{
244 ReadBuf *rb;
245
246 g_return_val_if_fail (string != NULL, NULL)do { if ((string != ((void*)0))) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "string != NULL"
); return (((void*)0)); } } while (0)
;
247 g_return_val_if_fail (length >= 0, NULL)do { if ((length >= 0)) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "length >= 0"
); return (((void*)0)); } } while (0)
;
248
249 rb = g_new0 (ReadBuf, 1)((ReadBuf *) g_malloc0_n ((1), sizeof (ReadBuf)));
250 /* rb->file = NULL; */
251 /* rb->stream = NULL; */
252 rb->uri = g_strdup (uri)g_strdup_inline (uri);
253 rb->buf = (char *) string;
254 /* rb->buf_needs_free = FALSE; */
255 /* rb->past_first_read = FALSE; */
256 /* rb->eof = FALSE; */
257 rb->size = length;
258 /* rb->pos = 0; */
259
260 return rb;
261}
262
263static gboolean
264readbuf_rewind (ReadBuf *rb, GError **error)
265{
266 GError *local_error;
267
268 rb->eof = FALSE(0);
269 rb->pos = 0;
270
271 if (!rb->past_first_read)
272 return TRUE(!(0));
273
274 rb->size = 0;
275
276 if (g_seekable_seek (G_SEEKABLE (rb->stream)((((GSeekable*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((rb->stream)), ((g_seekable_get_type ()))))))
,
277 0, G_SEEK_SET, NULL((void*)0), NULL((void*)0)))
278 return TRUE(!(0));
279
280 g_object_unref (rb->stream);
281 local_error = NULL((void*)0);
282 rb->stream = g_file_read (rb->file, NULL((void*)0), &local_error);
283
284 if (rb->stream == NULL((void*)0)) {
285 g_set_error (
286 error, CAFE_DESKTOP_ITEM_ERRORcafe_desktop_item_error_quark (),
287 CAFE_DESKTOP_ITEM_ERROR_CANNOT_OPEN,
288 _("Error rewinding file '%s': %s")((char *) g_dgettext ("cafe-desktop", "Error rewinding file '%s': %s"
))
,
289 rb->uri, local_error->message);
290 g_error_free (local_error);
291
292 return FALSE(0);
293 }
294
295 return TRUE(!(0));
296}
297
298static void
299readbuf_close (ReadBuf *rb)
300{
301 if (rb->stream != NULL((void*)0))
302 g_object_unref (rb->stream);
303 if (rb->file != NULL((void*)0))
304 g_object_unref (rb->file);
305 g_free (rb->uri);
306 if (rb->buf_needs_free)
307 g_free (rb->buf);
308 g_free (rb);
309}
310
311static CafeDesktopItemType
312type_from_string (const char *type)
313{
314 if (!type)
315 return CAFE_DESKTOP_ITEM_TYPE_NULL;
316
317 switch (type [0]) {
318 case 'A':
319 if (!strcmp (type, "Application"))
320 return CAFE_DESKTOP_ITEM_TYPE_APPLICATION;
321 break;
322 case 'L':
323 if (!strcmp (type, "Link"))
324 return CAFE_DESKTOP_ITEM_TYPE_LINK;
325 break;
326 case 'F':
327 if (!strcmp (type, "FSDevice"))
328 return CAFE_DESKTOP_ITEM_TYPE_FSDEVICE;
329 break;
330 case 'M':
331 if (!strcmp (type, "MimeType"))
332 return CAFE_DESKTOP_ITEM_TYPE_MIME_TYPE;
333 break;
334 case 'D':
335 if (!strcmp (type, "Directory"))
336 return CAFE_DESKTOP_ITEM_TYPE_DIRECTORY;
337 break;
338 case 'S':
339 if (!strcmp (type, "Service"))
340 return CAFE_DESKTOP_ITEM_TYPE_SERVICE;
341
342 else if (!strcmp (type, "ServiceType"))
343 return CAFE_DESKTOP_ITEM_TYPE_SERVICE_TYPE;
344 break;
345 default:
346 break;
347 }
348
349 return CAFE_DESKTOP_ITEM_TYPE_OTHER;
350}
351
352/**
353 * cafe_desktop_item_new:
354 *
355 * Creates a CafeDesktopItem object. The reference count on the returned value is set to '1'.
356 *
357 * Returns: The new CafeDesktopItem
358 */
359CafeDesktopItem *
360cafe_desktop_item_new (void)
361{
362 CafeDesktopItem *retval;
363
364 _cafe_desktop_init_i18n ();
365
366 retval = g_new0 (CafeDesktopItem, 1)((CafeDesktopItem *) g_malloc0_n ((1), sizeof (CafeDesktopItem
)))
;
367
368 retval->refcount++;
369
370 retval->main_hash = g_hash_table_new_full (g_str_hash, g_str_equal,
371 (GDestroyNotify) g_free,
372 (GDestroyNotify) g_free);
373
374 /* These are guaranteed to be set */
375 cafe_desktop_item_set_string (retval,
376 CAFE_DESKTOP_ITEM_NAME"Name",
377 /* Translators: the "name" mentioned
378 * here is the name of an application or
379 * a document */
380 _("No name")((char *) g_dgettext ("cafe-desktop", "No name")));
381 cafe_desktop_item_set_string (retval,
382 CAFE_DESKTOP_ITEM_ENCODING"Encoding",
383 "UTF-8");
384 cafe_desktop_item_set_string (retval,
385 CAFE_DESKTOP_ITEM_VERSION"Version",
386 "1.0");
387
388 retval->launch_time = 0;
389
390 return retval;
391}
392
393static Section *
394dup_section (Section *sec)
395{
396 GList *li;
397 Section *retval = g_new0 (Section, 1)((Section *) g_malloc0_n ((1), sizeof (Section)));
398
399 retval->name = g_strdup (sec->name)g_strdup_inline (sec->name);
400
401 retval->keys = g_list_copy (sec->keys);
402 for (li = retval->keys; li != NULL((void*)0); li = li->next)
403 li->data = g_strdup (li->data)g_strdup_inline (li->data);
404
405 return retval;
406}
407
408static void
409copy_string_hash (gpointer key, gpointer value, gpointer user_data)
410{
411 GHashTable *copy = user_data;
412 g_hash_table_replace (copy,
413 g_strdup (key)g_strdup_inline (key),
414 g_strdup (value)g_strdup_inline (value));
415}
416
417
418/**
419 * cafe_desktop_item_copy:
420 * @item: The item to be copied
421 *
422 * Creates a copy of a CafeDesktopItem. The new copy has a refcount of 1.
423 * Note: Section stack is NOT copied.
424 *
425 * Returns: The new copy
426 */
427CafeDesktopItem *
428cafe_desktop_item_copy (const CafeDesktopItem *item)
429{
430 GList *li;
431 CafeDesktopItem *retval;
432
433 g_return_val_if_fail (item != NULL, NULL)do { if ((item != ((void*)0))) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "item != NULL");
return (((void*)0)); } } while (0)
;
434 g_return_val_if_fail (item->refcount > 0, NULL)do { if ((item->refcount > 0)) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "item->refcount > 0"
); return (((void*)0)); } } while (0)
;
435
436 retval = cafe_desktop_item_new ();
437
438 retval->type = item->type;
439 retval->modified = item->modified;
440 retval->location = g_strdup (item->location)g_strdup_inline (item->location);
441 retval->mtime = item->mtime;
442 retval->launch_time = item->launch_time;
443
444 /* Languages */
445 retval->languages = g_list_copy (item->languages);
446 for (li = retval->languages; li != NULL((void*)0); li = li->next)
447 li->data = g_strdup (li->data)g_strdup_inline (li->data);
448
449 /* Keys */
450 retval->keys = g_list_copy (item->keys);
451 for (li = retval->keys; li != NULL((void*)0); li = li->next)
452 li->data = g_strdup (li->data)g_strdup_inline (li->data);
453
454 /* Sections */
455 retval->sections = g_list_copy (item->sections);
456 for (li = retval->sections; li != NULL((void*)0); li = li->next)
457 li->data = dup_section (li->data);
458
459 retval->main_hash = g_hash_table_new_full (g_str_hash, g_str_equal,
460 (GDestroyNotify) g_free,
461 (GDestroyNotify) g_free);
462
463 g_hash_table_foreach (item->main_hash,
464 copy_string_hash,
465 retval->main_hash);
466
467 return retval;
468}
469
470static void
471read_sort_order (CafeDesktopItem *item, GFile *dir)
472{
473 GFile *child;
474 char buf[BUFSIZ8192];
475 GString *str;
476 ReadBuf *rb;
477
478 child = g_file_get_child (dir, ".order");
479
480 rb = readbuf_open (child, NULL((void*)0));
481 g_object_unref (child);
482
483 if (rb == NULL((void*)0))
484 return;
485
486 str = NULL((void*)0);
487 while (readbuf_gets (buf, sizeof (buf), rb) != NULL((void*)0)) {
488 if (str == NULL((void*)0))
489 str = g_string_new (buf);
490 else
491 g_string_append (str, buf)(__builtin_constant_p (buf) ? __extension__ ({ const char * const
__val = (buf); g_string_append_len_inline (str, __val, (__val
!= ((void*)0)) ? (gssize) strlen (((__val) + !(__val))) : (gssize
) -1); }) : g_string_append_len_inline (str, buf, (gssize) -1
))
;
492 g_string_append_c (str, ';')g_string_append_c_inline (str, ';');
493 }
494 readbuf_close (rb);
495 if (str != NULL((void*)0)) {
496 cafe_desktop_item_set_string (item, CAFE_DESKTOP_ITEM_SORT_ORDER"SortOrder",
497 str->str);
498 g_string_free (str, TRUE)(__builtin_constant_p ((!(0))) ? (((!(0))) ? (g_string_free) (
(str), ((!(0)))) : g_string_free_and_steal (str)) : (g_string_free
) ((str), ((!(0)))))
;
499 }
500}
501
502static CafeDesktopItem *
503make_fake_directory (GFile *dir)
504{
505 CafeDesktopItem *item;
506 GFile *child;
507
508 item = cafe_desktop_item_new ();
509 cafe_desktop_item_set_entry_type (item,
510 CAFE_DESKTOP_ITEM_TYPE_DIRECTORY);
511
512
513 item->mtime = DONT_UPDATE_MTIME((time_t)-2); /* it doesn't exist, we know that */
514 child = g_file_get_child (dir, ".directory");
515 cafe_desktop_item_set_location_gfile (item, child);
516 item->mtime = 0;
517 g_object_unref (child);
518
519 read_sort_order (item, dir);
520
521 return item;
522}
523
524/**
525 * cafe_desktop_item_new_from_file:
526 * @file: The filename or directory path to load the CafeDesktopItem from
527 * @flags: Flags to influence the loading process
528 *
529 * This function loads 'file' and turns it into a CafeDesktopItem.
530 *
531 * Returns: The newly loaded item.
532 */
533CafeDesktopItem *
534cafe_desktop_item_new_from_file (const char *file,
535 CafeDesktopItemLoadFlags flags,
536 GError **error)
537{
538 CafeDesktopItem *retval;
539 GFile *gfile;
540
541 g_return_val_if_fail (file != NULL, NULL)do { if ((file != ((void*)0))) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "file != NULL");
return (((void*)0)); } } while (0)
;
542
543 gfile = g_file_new_for_path (file);
544 retval = cafe_desktop_item_new_from_gfile (gfile, flags, error);
545 g_object_unref (gfile);
546
547 return retval;
548}
549
550/**
551 * cafe_desktop_item_new_from_uri:
552 * @uri: URI to load the CafeDesktopItem from
553 * @flags: Flags to influence the loading process
554 *
555 * This function loads 'uri' and turns it into a CafeDesktopItem.
556 *
557 * Returns: The newly loaded item.
558 */
559CafeDesktopItem *
560cafe_desktop_item_new_from_uri (const char *uri,
561 CafeDesktopItemLoadFlags flags,
562 GError **error)
563{
564 CafeDesktopItem *retval;
565 GFile *file;
566
567 g_return_val_if_fail (uri != NULL, NULL)do { if ((uri != ((void*)0))) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "uri != NULL"); return
(((void*)0)); } } while (0)
;
568
569 file = g_file_new_for_uri (uri);
570 retval = cafe_desktop_item_new_from_gfile (file, flags, error);
571 g_object_unref (file);
572
573 return retval;
574}
575
576static CafeDesktopItem *
577cafe_desktop_item_new_from_gfile (GFile *file,
578 CafeDesktopItemLoadFlags flags,
579 GError **error)
580{
581 CafeDesktopItem *retval;
582 GFile *subfn;
583 GFileInfo *info;
584 GFileType type;
585 GFile *parent;
586 time_t mtime = 0;
587 ReadBuf *rb;
588
589 g_return_val_if_fail (file != NULL, NULL)do { if ((file != ((void*)0))) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "file != NULL");
return (((void*)0)); } } while (0)
;
590
591 info = g_file_query_info (file,
592 G_FILE_ATTRIBUTE_STANDARD_TYPE"standard::type"","G_FILE_ATTRIBUTE_TIME_MODIFIED"time::modified",
593 G_FILE_QUERY_INFO_NONE, NULL((void*)0), error);
594 if (info == NULL((void*)0))
595 return NULL((void*)0);
596
597 type = g_file_info_get_file_type (info);
598
599 if (type != G_FILE_TYPE_REGULAR && type != G_FILE_TYPE_DIRECTORY) {
600 char *uri;
601
602 uri = g_file_get_uri (file);
603 g_set_error (error,
604 /* FIXME: better errors */
605 CAFE_DESKTOP_ITEM_ERRORcafe_desktop_item_error_quark (),
606 CAFE_DESKTOP_ITEM_ERROR_INVALID_TYPE,
607 _("File '%s' is not a regular file or directory.")((char *) g_dgettext ("cafe-desktop", "File '%s' is not a regular file or directory."
))
,
608 uri);
609
610 g_free (uri);
611 g_object_unref (info);
612
613 return NULL((void*)0);
614 }
615
616 mtime = g_file_info_get_attribute_uint64 (info,
617 G_FILE_ATTRIBUTE_TIME_MODIFIED"time::modified");
618
619 g_object_unref (info);
620
621 if (type == G_FILE_TYPE_DIRECTORY) {
622 GFile *child;
623 GFileInfo *child_info;
624
625 child = g_file_get_child (file, ".directory");
626 child_info = g_file_query_info (child,
627 G_FILE_ATTRIBUTE_TIME_MODIFIED"time::modified",
628 G_FILE_QUERY_INFO_NONE,
629 NULL((void*)0), NULL((void*)0));
630
631 if (child_info == NULL((void*)0)) {
632 g_object_unref (child);
633
634 if (flags & CAFE_DESKTOP_ITEM_LOAD_ONLY_IF_EXISTS) {
635 return NULL((void*)0);
636 } else {
637 return make_fake_directory (file);
638 }
639 }
640
641 mtime = g_file_info_get_attribute_uint64 (child_info,
642 G_FILE_ATTRIBUTE_TIME_MODIFIED"time::modified");
643 g_object_unref (child_info);
644
645 subfn = child;
646 } else {
647 subfn = g_file_dup (file);
648 }
649
650 rb = readbuf_open (subfn, error);
651
652 if (rb == NULL((void*)0)) {
653 g_object_unref (subfn);
654 return NULL((void*)0);
655 }
656
657 retval = ditem_load (rb,
658 (flags & CAFE_DESKTOP_ITEM_LOAD_NO_TRANSLATIONS) != 0,
659 error);
660
661 if (retval == NULL((void*)0)) {
662 g_object_unref (subfn);
663 return NULL((void*)0);
664 }
665
666 if (flags & CAFE_DESKTOP_ITEM_LOAD_ONLY_IF_EXISTS &&
667 ! cafe_desktop_item_exists (retval)) {
668 cafe_desktop_item_unref (retval);
669 g_object_unref (subfn);
670 return NULL((void*)0);
671 }
672
673 retval->mtime = DONT_UPDATE_MTIME((time_t)-2);
674 cafe_desktop_item_set_location_gfile (retval, subfn);
675 retval->mtime = mtime;
676
677 parent = g_file_get_parent (file);
678 if (parent != NULL((void*)0)) {
679 read_sort_order (retval, parent);
680 g_object_unref (parent);
681 }
682
683 g_object_unref (subfn);
684
685 return retval;
686}
687
688/**
689 * cafe_desktop_item_new_from_string:
690 * @string: string to load the CafeDesktopItem from
691 * @length: length of string, or -1 to use strlen
692 * @flags: Flags to influence the loading process
693 * @error: place to put errors
694 *
695 * This function turns the contents of the string into a CafeDesktopItem.
696 *
697 * Returns: The newly loaded item.
698 */
699CafeDesktopItem *
700cafe_desktop_item_new_from_string (const char *uri,
701 const char *string,
702 gssize length,
703 CafeDesktopItemLoadFlags flags,
704 GError **error)
705{
706 CafeDesktopItem *retval;
707 ReadBuf *rb;
708
709 g_return_val_if_fail (string != NULL, NULL)do { if ((string != ((void*)0))) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "string != NULL"
); return (((void*)0)); } } while (0)
;
710 g_return_val_if_fail (length >= -1, NULL)do { if ((length >= -1)) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "length >= -1"
); return (((void*)0)); } } while (0)
;
711
712 if (length == -1) {
713 length = strlen (string);
714 }
715
716 rb = readbuf_new_from_string (uri, string, length);
717
718 retval = ditem_load (rb,
719 (flags & CAFE_DESKTOP_ITEM_LOAD_NO_TRANSLATIONS) != 0,
720 error);
721
722 if (retval == NULL((void*)0)) {
723 return NULL((void*)0);
724 }
725
726 /* FIXME: Sort order? */
727
728 return retval;
729}
730
731static char *
732lookup_desktop_file_in_data_dir (const char *desktop_file,
733 const char *data_dir)
734{
735 char *path;
736
737 path = g_build_filename (data_dir, "applications", desktop_file, NULL((void*)0));
738 if (!g_file_test (path, G_FILE_TEST_EXISTS)) {
739 g_free (path);
740 return NULL((void*)0);
741 }
742 return path;
743}
744
745static char *
746file_from_basename (const char *basename)
747{
748 const char * const *system_data_dirs;
749 const char *user_data_dir;
750 char *retval;
751 int i;
752
753 user_data_dir = g_get_user_data_dir ();
754 system_data_dirs = g_get_system_data_dirs ();
755
756 if ((retval = lookup_desktop_file_in_data_dir (basename, user_data_dir))) {
757 return retval;
758 }
759 for (i = 0; system_data_dirs[i]; i++) {
760 if ((retval = lookup_desktop_file_in_data_dir (basename, system_data_dirs[i]))) {
761 return retval;
762 }
763 }
764 return NULL((void*)0);
765}
766
767/**
768 * cafe_desktop_item_new_from_basename:
769 * @basename: The basename of the CafeDesktopItem to load.
770 * @flags: Flags to influence the loading process
771 *
772 * This function loads 'basename' from a system data directory and
773 * returns its CafeDesktopItem.
774 *
775 * Returns: The newly loaded item.
776 */
777CafeDesktopItem *
778cafe_desktop_item_new_from_basename (const char *basename,
779 CafeDesktopItemLoadFlags flags,
780 GError **error)
781{
782 CafeDesktopItem *retval;
783 char *file;
784
785 g_return_val_if_fail (basename != NULL, NULL)do { if ((basename != ((void*)0))) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "basename != NULL"
); return (((void*)0)); } } while (0)
;
786
787 if (!(file = file_from_basename (basename))) {
788 g_set_error (error,
789 CAFE_DESKTOP_ITEM_ERRORcafe_desktop_item_error_quark (),
790 CAFE_DESKTOP_ITEM_ERROR_CANNOT_OPEN,
791 _("Cannot find file '%s'")((char *) g_dgettext ("cafe-desktop", "Cannot find file '%s'"
))
,
792 basename);
793 return NULL((void*)0);
794 }
795
796 retval = cafe_desktop_item_new_from_file (file, flags, error);
797 g_free (file);
798
799 return retval;
800}
801
802/**
803 * cafe_desktop_item_save:
804 * @item: A desktop item
805 * @under: A new uri (location) for this #CafeDesktopItem
806 * @force: Save even if it wasn't modified
807 * @error: #GError return
808 *
809 * Writes the specified item to disk. If the 'under' is NULL, the original
810 * location is used. It sets the location of this entry to point to the
811 * new location.
812 *
813 * Returns: boolean. %TRUE if the file was saved, %FALSE otherwise
814 */
815gboolean
816cafe_desktop_item_save (CafeDesktopItem *item,
817 const char *under,
818 gboolean force,
819 GError **error)
820{
821 const char *uri;
822
823 if (under == NULL((void*)0) &&
824 ! force &&
825 ! item->modified)
826 return TRUE(!(0));
827
828 if (under == NULL((void*)0))
829 uri = item->location;
830 else
831 uri = under;
832
833 if (uri == NULL((void*)0)) {
834 g_set_error (error,
835 CAFE_DESKTOP_ITEM_ERRORcafe_desktop_item_error_quark (),
836 CAFE_DESKTOP_ITEM_ERROR_NO_FILENAME,
837 _("No filename to save to")((char *) g_dgettext ("cafe-desktop", "No filename to save to"
))
);
838 return FALSE(0);
839 }
840
841 if ( ! ditem_save (item, uri, error))
842 return FALSE(0);
843
844 item->modified = FALSE(0);
845 item->mtime = time (NULL((void*)0));
846
847 return TRUE(!(0));
848}
849
850/**
851 * cafe_desktop_item_ref:
852 * @item: A desktop item
853 *
854 * Description: Increases the reference count of the specified item.
855 *
856 * Returns: the newly referenced @item
857 */
858CafeDesktopItem *
859cafe_desktop_item_ref (CafeDesktopItem *item)
860{
861 g_return_val_if_fail (item != NULL, NULL)do { if ((item != ((void*)0))) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "item != NULL");
return (((void*)0)); } } while (0)
;
862
863 item->refcount++;
864
865 return item;
866}
867
868static void
869free_section (gpointer data,
870 gpointer user_data G_GNUC_UNUSED__attribute__ ((__unused__)))
871{
872 Section *section = data;
873
874 g_free (section->name);
875 section->name = NULL((void*)0);
876
877 g_list_foreach (section->keys, (GFunc)g_free, NULL((void*)0));
878 g_list_free (section->keys);
879 section->keys = NULL((void*)0);
880
881 g_free (section);
882}
883
884/**
885 * cafe_desktop_item_unref:
886 * @item: A desktop item
887 *
888 * Decreases the reference count of the specified item, and destroys the item if there are no more references left.
889 */
890void
891cafe_desktop_item_unref (CafeDesktopItem *item)
892{
893 g_return_if_fail (item != NULL)do { if ((item != ((void*)0))) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "item != NULL");
return; } } while (0)
;
894 g_return_if_fail (item->refcount > 0)do { if ((item->refcount > 0)) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "item->refcount > 0"
); return; } } while (0)
;
895
896 item->refcount--;
897
898 if(item->refcount != 0)
899 return;
900
901 g_list_foreach (item->languages, (GFunc)g_free, NULL((void*)0));
902 g_list_free (item->languages);
903 item->languages = NULL((void*)0);
904
905 g_list_foreach (item->keys, (GFunc)g_free, NULL((void*)0));
906 g_list_free (item->keys);
907 item->keys = NULL((void*)0);
908
909 g_list_foreach (item->sections, free_section, NULL((void*)0));
910 g_list_free (item->sections);
911 item->sections = NULL((void*)0);
912
913 g_hash_table_destroy (item->main_hash);
914 item->main_hash = NULL((void*)0);
915
916 g_free (item->location);
917 item->location = NULL((void*)0);
918
919 g_free (item);
920}
921
922static Section *
923find_section (CafeDesktopItem *item, const char *section)
924{
925 GList *li;
926 Section *sec;
927
928 if (section == NULL((void*)0))
929 return NULL((void*)0);
930 if (strcmp (section, "Desktop Entry") == 0)
931 return NULL((void*)0);
932
933 for (li = item->sections; li != NULL((void*)0); li = li->next) {
934 sec = li->data;
935 if (strcmp (sec->name, section) == 0)
936 return sec;
937 }
938
939 sec = g_new0 (Section, 1)((Section *) g_malloc0_n ((1), sizeof (Section)));
940 sec->name = g_strdup (section)g_strdup_inline (section);
941 sec->keys = NULL((void*)0);
942
943 item->sections = g_list_append (item->sections, sec);
944
945 /* Don't mark the item modified, this is just an empty section,
946 * it won't be saved even */
947
948 return sec;
949}
950
951static Section *
952section_from_key (CafeDesktopItem *item, const char *key)
953{
954 char *p;
955 char *name;
956 Section *sec;
957
958 if (key == NULL((void*)0))
959 return NULL((void*)0);
960
961 p = strchr (key, '/');
962 if (p == NULL((void*)0))
963 return NULL((void*)0);
964
965 name = g_strndup (key, p - key);
966
967 sec = find_section (item, name);
968
969 g_free (name);
970
971 return sec;
972}
973
974static const char *
975key_basename (const char *key)
976{
977 char *p = strrchr (key, '/');
978 if (p != NULL((void*)0))
979 return p+1;
980 else
981 return key;
982}
983
984
985static const char *
986lookup (const CafeDesktopItem *item, const char *key)
987{
988 return g_hash_table_lookup (item->main_hash, key);
989}
990
991static const char *
992lookup_locale (const CafeDesktopItem *item, const char *key, const char *locale)
993{
994 if (locale == NULL((void*)0) ||
995 strcmp (locale, "C") == 0) {
996 return lookup (item, key);
997 } else {
998 const char *ret;
999 char *full = g_strdup_printf ("%s[%s]", key, locale);
1000 ret = lookup (item, full);
1001 g_free (full);
1002 return ret;
1003 }
1004}
1005
1006static const char *
1007lookup_best_locale (const CafeDesktopItem *item, const char *key)
1008{
1009 const char * const *langs_pointer;
1010 int i;
1011
1012 langs_pointer = g_get_language_names ();
1013 for (i = 0; langs_pointer[i] != NULL((void*)0); i++) {
1014 const char *ret = NULL((void*)0);
1015
1016 ret = lookup_locale (item, key, langs_pointer[i]);
1017 if (ret != NULL((void*)0))
1018 return ret;
1019 }
1020
1021 return NULL((void*)0);
1022}
1023
1024static void
1025set (CafeDesktopItem *item, const char *key, const char *value)
1026{
1027 Section *sec = section_from_key (item, key);
1028
1029 if (sec != NULL((void*)0)) {
1030 if (value != NULL((void*)0)) {
1031 if (g_hash_table_lookup (item->main_hash, key) == NULL((void*)0))
1032 sec->keys = g_list_append
1033 (sec->keys,
1034 g_strdup (key_basename (key))g_strdup_inline (key_basename (key)));
1035
1036 g_hash_table_replace (item->main_hash,
1037 g_strdup (key)g_strdup_inline (key),
1038 g_strdup (value)g_strdup_inline (value));
1039 } else {
1040 GList *list = g_list_find_custom
1041 (sec->keys, key_basename (key),
1042 (GCompareFunc)strcmp);
1043 if (list != NULL((void*)0)) {
1044 g_free (list->data);
1045 sec->keys =
1046 g_list_delete_link (sec->keys, list);
1047 }
1048 g_hash_table_remove (item->main_hash, key);
1049 }
1050 } else {
1051 if (value != NULL((void*)0)) {
1052 if (g_hash_table_lookup (item->main_hash, key) == NULL((void*)0))
1053 item->keys = g_list_append (item->keys,
1054 g_strdup (key)g_strdup_inline (key));
1055
1056 g_hash_table_replace (item->main_hash,
1057 g_strdup (key)g_strdup_inline (key),
1058 g_strdup (value)g_strdup_inline (value));
1059 } else {
1060 GList *list = g_list_find_custom
1061 (item->keys, key, (GCompareFunc)strcmp);
1062 if (list != NULL((void*)0)) {
1063 g_free (list->data);
1064 item->keys =
1065 g_list_delete_link (item->keys, list);
1066 }
1067 g_hash_table_remove (item->main_hash, key);
1068 }
1069 }
1070 item->modified = TRUE(!(0));
1071}
1072
1073static void
1074set_locale (CafeDesktopItem *item, const char *key,
1075 const char *locale, const char *value)
1076{
1077 if (locale == NULL((void*)0) ||
1078 strcmp (locale, "C") == 0) {
1079 set (item, key, value);
1080 } else {
1081 char *full = g_strdup_printf ("%s[%s]", key, locale);
1082 set (item, full, value);
1083 g_free (full);
1084
1085 /* add the locale to the list of languages if it wasn't there
1086 * before */
1087 if (g_list_find_custom (item->languages, locale,
1088 (GCompareFunc)strcmp) == NULL((void*)0))
1089 item->languages = g_list_prepend (item->languages,
1090 g_strdup (locale)g_strdup_inline (locale));
1091 }
1092}
1093
1094static char **
1095list_to_vector (GSList *list)
1096{
1097 int len = g_slist_length (list);
1098 char **argv;
1099 int i;
1100 GSList *li;
1101
1102 argv = g_new0 (char *, len+1)((char * *) g_malloc0_n ((len+1), sizeof (char *)));
1103
1104 for (i = 0, li = list;
1105 li != NULL((void*)0);
1106 li = li->next, i++) {
1107 argv[i] = g_strdup (li->data)g_strdup_inline (li->data);
1108 }
1109 argv[i] = NULL((void*)0);
1110
1111 return argv;
1112}
1113
1114static GSList *
1115make_args (GList *files)
1116{
1117 GSList *list = NULL((void*)0);
1118 GList *li;
1119
1120 for (li = files; li != NULL((void*)0); li = li->next) {
1121 GFile *gfile;
1122 const char *file = li->data;
1123 if (file == NULL((void*)0))
1124 continue;
1125 gfile = g_file_new_for_uri (file);
1126 list = g_slist_prepend (list, gfile);
1127 }
1128
1129 return g_slist_reverse (list);
1130}
1131
1132static void
1133free_args (GSList *list)
1134{
1135 GSList *li;
1136
1137 for (li = list; li != NULL((void*)0); li = li->next) {
1138 g_object_unref (G_FILE (li->data)((((GFile*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((li->data)), ((g_file_get_type ()))))))
);
1139 li->data = NULL((void*)0);
1140 }
1141 g_slist_free (list);
1142}
1143
1144static char *
1145escape_single_quotes (const char *s,
1146 gboolean in_single_quotes,
1147 gboolean in_double_quotes)
1148{
1149 const char *p;
1150 GString *gs;
1151 const char *pre = "";
1152 const char *post = "";
1153
1154 if ( ! in_single_quotes && ! in_double_quotes) {
1155 pre = "'";
1156 post = "'";
1157 } else if ( ! in_single_quotes && in_double_quotes) {
1158 pre = "\"'";
1159 post = "'\"";
1160 }
1161
1162 if (strchr (s, '\'') == NULL((void*)0)) {
1163 return g_strconcat (pre, s, post, NULL((void*)0));
1164 }
1165
1166 gs = g_string_new (pre);
1167
1168 for (p = s; *p != '\0'; p++) {
1169 if (*p == '\'')
1170 g_string_append (gs, "'\\''")(__builtin_constant_p ("'\\''") ? __extension__ ({ const char
* const __val = ("'\\''"); g_string_append_len_inline (gs, __val
, (__val != ((void*)0)) ? (gssize) strlen (((__val) + !(__val
))) : (gssize) -1); }) : g_string_append_len_inline (gs, "'\\''"
, (gssize) -1))
;
1171 else
1172 g_string_append_c (gs, *p)g_string_append_c_inline (gs, *p);
1173 }
1174
1175 g_string_append (gs, post)(__builtin_constant_p (post) ? __extension__ ({ const char * const
__val = (post); g_string_append_len_inline (gs, __val, (__val
!= ((void*)0)) ? (gssize) strlen (((__val) + !(__val))) : (gssize
) -1); }) : g_string_append_len_inline (gs, post, (gssize) -1
))
;
1176
1177 return g_string_free (gs, FALSE)(__builtin_constant_p ((0)) ? (((0)) ? (g_string_free) ((gs),
((0))) : g_string_free_and_steal (gs)) : (g_string_free) ((gs
), ((0))))
;
1178}
1179
1180typedef enum {
1181 URI_TO_STRING,
1182 URI_TO_LOCAL_PATH,
1183 URI_TO_LOCAL_DIRNAME,
1184 URI_TO_LOCAL_BASENAME
1185} ConversionType;
1186
1187static char *
1188convert_uri (GFile *file,
1189 ConversionType conversion)
1190{
1191 char *retval = NULL((void*)0);
1192
1193 switch (conversion) {
1194 case URI_TO_STRING:
1195 retval = g_file_get_uri (file);
1196 break;
1197 case URI_TO_LOCAL_PATH:
1198 retval = g_file_get_path (file);
1199 break;
1200 case URI_TO_LOCAL_DIRNAME:
1201 {
1202 char *local_path;
1203
1204 local_path = g_file_get_path (file);
1205 retval = g_path_get_dirname (local_path);
1206 g_free (local_path);
1207 }
1208 break;
1209 case URI_TO_LOCAL_BASENAME:
1210 retval = g_file_get_basename (file);
1211 break;
1212 default:
1213 g_assert_not_reached ()do { g_assertion_message_expr ("CafeDesktop", "cafe-desktop-item.c"
, 1213, ((const char*) (__func__)), ((void*)0)); } while (0)
;
1214 }
1215
1216 return retval;
1217}
1218
1219typedef enum {
1220 ADDED_NONE = 0,
1221 ADDED_SINGLE,
1222 ADDED_ALL
1223} AddedStatus;
1224
1225static AddedStatus
1226append_all_converted (GString *str,
1227 ConversionType conversion,
1228 GSList *args,
1229 gboolean in_single_quotes,
1230 gboolean in_double_quotes,
1231 AddedStatus added_status G_GNUC_UNUSED__attribute__ ((__unused__)))
1232{
1233 GSList *l;
1234
1235 for (l = args; l; l = l->next) {
1236 char *converted;
1237 char *escaped;
1238
1239 if (!(converted = convert_uri (l->data, conversion)))
1240 continue;
1241
1242 g_string_append (str, " ")(__builtin_constant_p (" ") ? __extension__ ({ const char * const
__val = (" "); g_string_append_len_inline (str, __val, (__val
!= ((void*)0)) ? (gssize) strlen (((__val) + !(__val))) : (gssize
) -1); }) : g_string_append_len_inline (str, " ", (gssize) -1
))
;
1243
1244 escaped = escape_single_quotes (converted,
1245 in_single_quotes,
1246 in_double_quotes);
1247 g_string_append (str, escaped)(__builtin_constant_p (escaped) ? __extension__ ({ const char
* const __val = (escaped); g_string_append_len_inline (str, __val
, (__val != ((void*)0)) ? (gssize) strlen (((__val) + !(__val
))) : (gssize) -1); }) : g_string_append_len_inline (str, escaped
, (gssize) -1))
;
1248
1249 g_free (escaped);
1250 g_free (converted);
1251 }
1252
1253 return ADDED_ALL;
1254}
1255
1256static AddedStatus
1257append_first_converted (GString *str,
1258 ConversionType conversion,
1259 GSList **arg_ptr,
1260 gboolean in_single_quotes,
1261 gboolean in_double_quotes,
1262 AddedStatus added_status)
1263{
1264 GSList *l;
1265 char *converted = NULL((void*)0);
1266 char *escaped;
1267
1268 for (l = *arg_ptr; l; l = l->next) {
1269 if ((converted = convert_uri (l->data, conversion)))
1270 break;
1271
1272 *arg_ptr = l->next;
1273 }
1274
1275 if (!converted)
1276 return added_status;
1277
1278 escaped = escape_single_quotes (converted, in_single_quotes, in_double_quotes);
1279 g_string_append (str, escaped)(__builtin_constant_p (escaped) ? __extension__ ({ const char
* const __val = (escaped); g_string_append_len_inline (str, __val
, (__val != ((void*)0)) ? (gssize) strlen (((__val) + !(__val
))) : (gssize) -1); }) : g_string_append_len_inline (str, escaped
, (gssize) -1))
;
1280 g_free (escaped);
1281 g_free (converted);
1282
1283 return added_status != ADDED_ALL ? ADDED_SINGLE : added_status;
1284}
1285
1286static gboolean
1287do_percent_subst (const CafeDesktopItem *item,
1288 const char *arg,
1289 GString *str,
1290 gboolean in_single_quotes,
1291 gboolean in_double_quotes,
1292 GSList *args,
1293 GSList **arg_ptr,
1294 AddedStatus *added_status)
1295{
1296 char *esc;
1297 const char *cs;
1298
1299 if (arg[0] != '%' || arg[1] == '\0') {
1300 return FALSE(0);
1301 }
1302
1303 switch (arg[1]) {
1304 case '%':
1305 g_string_append_c (str, '%')g_string_append_c_inline (str, '%');
1306 break;
1307 case 'U':
1308 *added_status = append_all_converted (str,
1309 URI_TO_STRING,
1310 args,
1311 in_single_quotes,
1312 in_double_quotes,
1313 *added_status);
1314 break;
1315 case 'F':
1316 *added_status = append_all_converted (str,
1317 URI_TO_LOCAL_PATH,
1318 args,
1319 in_single_quotes,
1320 in_double_quotes,
1321 *added_status);
1322 break;
1323 case 'N':
1324 *added_status = append_all_converted (str,
1325 URI_TO_LOCAL_BASENAME,
1326 args,
1327 in_single_quotes,
1328 in_double_quotes,
1329 *added_status);
1330 break;
1331 case 'D':
1332 *added_status = append_all_converted (str,
1333 URI_TO_LOCAL_DIRNAME,
1334 args,
1335 in_single_quotes,
1336 in_double_quotes,
1337 *added_status);
1338 break;
1339 case 'f':
1340 *added_status = append_first_converted (str,
1341 URI_TO_LOCAL_PATH,
1342 arg_ptr,
1343 in_single_quotes,
1344 in_double_quotes,
1345 *added_status);
1346 break;
1347 case 'u':
1348 *added_status = append_first_converted (str,
1349 URI_TO_STRING,
1350 arg_ptr,
1351 in_single_quotes,
1352 in_double_quotes,
1353 *added_status);
1354 break;
1355 case 'd':
1356 *added_status = append_first_converted (str,
1357 URI_TO_LOCAL_DIRNAME,
1358 arg_ptr,
1359 in_single_quotes,
1360 in_double_quotes,
1361 *added_status);
1362 break;
1363 case 'n':
1364 *added_status = append_first_converted (str,
1365 URI_TO_LOCAL_BASENAME,
1366 arg_ptr,
1367 in_single_quotes,
1368 in_double_quotes,
1369 *added_status);
1370 break;
1371 case 'm':
1372 /* Note: v0.9.4 of the spec says this is deprecated
1373 * and replace with --miniicon iconname */
1374 cs = cafe_desktop_item_get_string (item, CAFE_DESKTOP_ITEM_MINI_ICON"MiniIcon");
1375 if (cs != NULL((void*)0)) {
1376 g_string_append (str, "--miniicon=")(__builtin_constant_p ("--miniicon=") ? __extension__ ({ const
char * const __val = ("--miniicon="); g_string_append_len_inline
(str, __val, (__val != ((void*)0)) ? (gssize) strlen (((__val
) + !(__val))) : (gssize) -1); }) : g_string_append_len_inline
(str, "--miniicon=", (gssize) -1))
;
1377 esc = escape_single_quotes (cs, in_single_quotes, in_double_quotes);
1378 g_string_append (str, esc)(__builtin_constant_p (esc) ? __extension__ ({ const char * const
__val = (esc); g_string_append_len_inline (str, __val, (__val
!= ((void*)0)) ? (gssize) strlen (((__val) + !(__val))) : (gssize
) -1); }) : g_string_append_len_inline (str, esc, (gssize) -1
))
;
1379 }
1380 break;
1381 case 'i':
1382 /* Note: v0.9.4 of the spec says replace with --icon iconname */
1383 cs = cafe_desktop_item_get_string (item, CAFE_DESKTOP_ITEM_ICON"Icon");
1384 if (cs != NULL((void*)0)) {
1385 g_string_append (str, "--icon=")(__builtin_constant_p ("--icon=") ? __extension__ ({ const char
* const __val = ("--icon="); g_string_append_len_inline (str
, __val, (__val != ((void*)0)) ? (gssize) strlen (((__val) + !
(__val))) : (gssize) -1); }) : g_string_append_len_inline (str
, "--icon=", (gssize) -1))
;
1386 esc = escape_single_quotes (cs, in_single_quotes, in_double_quotes);
1387 g_string_append (str, esc)(__builtin_constant_p (esc) ? __extension__ ({ const char * const
__val = (esc); g_string_append_len_inline (str, __val, (__val
!= ((void*)0)) ? (gssize) strlen (((__val) + !(__val))) : (gssize
) -1); }) : g_string_append_len_inline (str, esc, (gssize) -1
))
;
1388 }
1389 break;
1390 case 'c':
1391 cs = cafe_desktop_item_get_localestring (item, CAFE_DESKTOP_ITEM_NAME"Name");
1392 if (cs != NULL((void*)0)) {
1393 esc = escape_single_quotes (cs, in_single_quotes, in_double_quotes);
1394 g_string_append (str, esc)(__builtin_constant_p (esc) ? __extension__ ({ const char * const
__val = (esc); g_string_append_len_inline (str, __val, (__val
!= ((void*)0)) ? (gssize) strlen (((__val) + !(__val))) : (gssize
) -1); }) : g_string_append_len_inline (str, esc, (gssize) -1
))
;
1395 g_free (esc);
1396 }
1397 break;
1398 case 'k':
1399 if (item->location != NULL((void*)0)) {
1400 esc = escape_single_quotes (item->location, in_single_quotes, in_double_quotes);
1401 g_string_append (str, esc)(__builtin_constant_p (esc) ? __extension__ ({ const char * const
__val = (esc); g_string_append_len_inline (str, __val, (__val
!= ((void*)0)) ? (gssize) strlen (((__val) + !(__val))) : (gssize
) -1); }) : g_string_append_len_inline (str, esc, (gssize) -1
))
;
1402 g_free (esc);
1403 }
1404 break;
1405 case 'v':
1406 cs = cafe_desktop_item_get_localestring (item, CAFE_DESKTOP_ITEM_DEV"Dev");
1407 if (cs != NULL((void*)0)) {
1408 esc = escape_single_quotes (cs, in_single_quotes, in_double_quotes);
1409 g_string_append (str, esc)(__builtin_constant_p (esc) ? __extension__ ({ const char * const
__val = (esc); g_string_append_len_inline (str, __val, (__val
!= ((void*)0)) ? (gssize) strlen (((__val) + !(__val))) : (gssize
) -1); }) : g_string_append_len_inline (str, esc, (gssize) -1
))
;
1410 g_free (esc);
1411 }
1412 break;
1413 default:
1414 /* Maintain special characters - e.g. "%20" */
1415 if (g_ascii_isdigit (arg [1])((g_ascii_table[(guchar) (arg [1])] & G_ASCII_DIGIT) != 0
)
)
1416 g_string_append_c (str, '%')g_string_append_c_inline (str, '%');
1417 return FALSE(0);
1418 }
1419
1420 return TRUE(!(0));
1421}
1422
1423static char *
1424expand_string (const CafeDesktopItem *item,
1425 const char *s,
1426 GSList *args,
1427 GSList **arg_ptr,
1428 AddedStatus *added_status)
1429{
1430 const char *p;
1431 gboolean escape = FALSE(0);
1432 gboolean single_quot = FALSE(0);
1433 gboolean double_quot = FALSE(0);
1434 GString *gs = g_string_new (NULL((void*)0));
1435
1436 for (p = s; *p != '\0'; p++) {
1437 if (escape) {
1438 escape = FALSE(0);
1439 g_string_append_c (gs, *p)g_string_append_c_inline (gs, *p);
1440 } else if (*p == '\\') {
1441 if ( ! single_quot)
1442 escape = TRUE(!(0));
1443 g_string_append_c (gs, *p)g_string_append_c_inline (gs, *p);
1444 } else if (*p == '\'') {
1445 g_string_append_c (gs, *p)g_string_append_c_inline (gs, *p);
1446 if ( ! single_quot && ! double_quot) {
1447 single_quot = TRUE(!(0));
1448 } else if (single_quot) {
1449 single_quot = FALSE(0);
1450 }
1451 } else if (*p == '"') {
1452 g_string_append_c (gs, *p)g_string_append_c_inline (gs, *p);
1453 if ( ! single_quot && ! double_quot) {
1454 double_quot = TRUE(!(0));
1455 } else if (double_quot) {
1456 double_quot = FALSE(0);
1457 }
1458 } else if (*p == '%') {
1459 if (do_percent_subst (item, p, gs,
1460 single_quot, double_quot,
1461 args, arg_ptr,
1462 added_status)) {
1463 p++;
1464 }
1465 } else {
1466 g_string_append_c (gs, *p)g_string_append_c_inline (gs, *p);
1467 }
1468 }
1469 return g_string_free (gs, FALSE)(__builtin_constant_p ((0)) ? (((0)) ? (g_string_free) ((gs),
((0))) : g_string_free_and_steal (gs)) : (g_string_free) ((gs
), ((0))))
;
1470}
1471
1472#ifdef HAVE_STARTUP_NOTIFICATION
1473static void
1474sn_error_trap_push (SnDisplay *display G_GNUC_UNUSED__attribute__ ((__unused__)),
1475 Display *xdisplay G_GNUC_UNUSED__attribute__ ((__unused__)))
1476{
1477 CdkDisplay *cdkdisplay;
1478
1479 cdkdisplay = cdk_display_get_default ();
1480 cdk_x11_display_error_trap_push (cdkdisplay);
1481}
1482
1483static void
1484sn_error_trap_pop (SnDisplay *display G_GNUC_UNUSED__attribute__ ((__unused__)),
1485 Display *xdisplay G_GNUC_UNUSED__attribute__ ((__unused__)))
1486{
1487 CdkDisplay *cdkdisplay;
1488
1489 cdkdisplay = cdk_display_get_default ();
1490 cdk_x11_display_error_trap_pop_ignored (cdkdisplay);
1491}
1492
1493static char **
1494make_spawn_environment_for_sn_context (SnLauncherContext *sn_context,
1495 char **envp)
1496{
1497 char **retval;
1498 char **freeme;
1499 int i, j;
1500 int desktop_startup_id_len;
1501
1502 retval = freeme = NULL((void*)0);
1503
1504 if (envp == NULL((void*)0)) {
1505 envp = freeme = g_listenv ();
1506 for (i = 0; envp[i]; i++) {
1507 char *name = envp[i];
1508
1509 envp[i] = g_strjoin ("=", name, g_getenv (name), NULL((void*)0));
1510 g_free (name);
1511 }
1512 } else {
1513 for (i = 0; envp[i]; i++)
1514 ;
1515 }
1516
1517 retval = g_new (char *, i + 2)((char * *) g_malloc_n ((i + 2), sizeof (char *)));
1518
1519 desktop_startup_id_len = strlen ("DESKTOP_STARTUP_ID");
1520
1521 for (i = 0, j = 0; envp[i]; i++) {
1522 if (strncmp (envp[i], "DESKTOP_STARTUP_ID", desktop_startup_id_len) != 0) {
1523 retval[j] = g_strdup (envp[i])g_strdup_inline (envp[i]);
1524 ++j;
1525 }
1526 }
1527
1528 retval[j] = g_strdup_printf ("DESKTOP_STARTUP_ID=%s",
1529 sn_launcher_context_get_startup_id (sn_context));
1530 ++j;
1531 retval[j] = NULL((void*)0);
1532
1533 g_strfreev (freeme);
1534
1535 return retval;
1536}
1537
1538/* This should be fairly long, as it's confusing to users if a startup
1539 * ends when it shouldn't (it appears that the startup failed, and
1540 * they have to relaunch the app). Also the timeout only matters when
1541 * there are bugs and apps don't end their own startup sequence.
1542 *
1543 * This timeout is a "last resort" timeout that ignores whether the
1544 * startup sequence has shown activity or not. Croma and the
1545 * tasklist have smarter, and correspondingly able-to-be-shorter
1546 * timeouts. The reason our timeout is dumb is that we don't monitor
1547 * the sequence (don't use an SnMonitorContext)
1548 */
1549#define STARTUP_TIMEOUT_LENGTH_SEC30 30 /* seconds */
1550#define STARTUP_TIMEOUT_LENGTH(30 * 1000) (STARTUP_TIMEOUT_LENGTH_SEC30 * 1000)
1551
1552typedef struct
1553{
1554 CdkScreen *screen;
1555 GSList *contexts;
1556 guint timeout_id;
1557} StartupTimeoutData;
1558
1559static void
1560free_startup_timeout (void *data)
1561{
1562 StartupTimeoutData *std = data;
1563
1564 g_slist_foreach (std->contexts,
1565 (GFunc) sn_launcher_context_unref,
1566 NULL((void*)0));
1567 g_slist_free (std->contexts);
1568
1569 if (std->timeout_id != 0) {
1570 g_source_remove (std->timeout_id);
1571 std->timeout_id = 0;
1572 }
1573
1574 g_free (std);
1575}
1576
1577static gboolean
1578startup_timeout (void *data)
1579{
1580 StartupTimeoutData *std = data;
1581 GSList *tmp;
1582 int min_timeout;
1583
1584 min_timeout = STARTUP_TIMEOUT_LENGTH(30 * 1000);
1585 gint64 now = g_get_real_time ();
1586
1587 tmp = std->contexts;
1588 while (tmp != NULL((void*)0)) {
1589 SnLauncherContext *sn_context = tmp->data;
1590 GSList *next = tmp->next;
1591 double elapsed;
1592 time_t tv_sec;
1593 suseconds_t tv_usec;
1594 gint64 tv;
1595
1596 sn_launcher_context_get_last_active_time (sn_context, &tv_sec, &tv_usec);
1597 tv = (tv_sec * G_USEC_PER_SEC1000000) + tv_usec;
1598 elapsed = (double) (now - tv) / 1000.0;
1599
1600 if (elapsed >= STARTUP_TIMEOUT_LENGTH(30 * 1000)) {
1601 std->contexts = g_slist_remove (std->contexts,
1602 sn_context);
1603 sn_launcher_context_complete (sn_context);
1604 sn_launcher_context_unref (sn_context);
1605 } else {
1606 min_timeout = MIN (min_timeout, (STARTUP_TIMEOUT_LENGTH - elapsed))(((min_timeout) < (((30 * 1000) - elapsed))) ? (min_timeout
) : (((30 * 1000) - elapsed)))
;
1607 }
1608
1609 tmp = next;
1610 }
1611
1612 /* we'll use seconds for the timeout */
1613 if (min_timeout < 1000)
1614 min_timeout = 1000;
1615
1616 if (std->contexts == NULL((void*)0)) {
1617 std->timeout_id = 0;
1618 } else {
1619 std->timeout_id = g_timeout_add_seconds (min_timeout / 1000,
1620 startup_timeout,
1621 std);
1622 }
1623
1624 /* always remove this one, but we may have reinstalled another one. */
1625 return FALSE(0);
1626}
1627
1628static void
1629add_startup_timeout (CdkScreen *screen,
1630 SnLauncherContext *sn_context)
1631{
1632 StartupTimeoutData *data;
1633
1634 data = g_object_get_data (G_OBJECT (screen)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((screen)), (((GType) ((20) << (2))))))))
, "cafe-startup-data");
1635 if (data == NULL((void*)0)) {
1636 data = g_new (StartupTimeoutData, 1)((StartupTimeoutData *) g_malloc_n ((1), sizeof (StartupTimeoutData
)))
;
1637 data->screen = screen;
1638 data->contexts = NULL((void*)0);
1639 data->timeout_id = 0;
1640
1641 g_object_set_data_full (G_OBJECT (screen)((((GObject*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((screen)), (((GType) ((20) << (2))))))))
, "cafe-startup-data",
1642 data, free_startup_timeout);
1643 }
1644
1645 sn_launcher_context_ref (sn_context);
1646 data->contexts = g_slist_prepend (data->contexts, sn_context);
1647
1648 if (data->timeout_id == 0) {
1649 data->timeout_id = g_timeout_add_seconds (
1650 STARTUP_TIMEOUT_LENGTH_SEC30,
1651 startup_timeout,
1652 data);
1653 }
1654}
1655#endif /* HAVE_STARTUP_NOTIFICATION */
1656
1657static inline char *
1658stringify_uris (GSList *args)
1659{
1660 GString *str;
1661
1662 str = g_string_new (NULL((void*)0));
1663
1664 append_all_converted (str, URI_TO_STRING, args, FALSE(0), FALSE(0), ADDED_NONE);
1665
1666 return g_string_free (str, FALSE)(__builtin_constant_p ((0)) ? (((0)) ? (g_string_free) ((str)
, ((0))) : g_string_free_and_steal (str)) : (g_string_free) (
(str), ((0))))
;
1667}
1668
1669static inline char *
1670stringify_files (GSList *args)
1671{
1672 GString *str;
1673
1674 str = g_string_new (NULL((void*)0));
1675
1676 append_all_converted (str, URI_TO_LOCAL_PATH, args, FALSE(0), FALSE(0), ADDED_NONE);
1677
1678 return g_string_free (str, FALSE)(__builtin_constant_p ((0)) ? (((0)) ? (g_string_free) ((str)
, ((0))) : g_string_free_and_steal (str)) : (g_string_free) (
(str), ((0))))
;
1679}
1680
1681static char **
1682make_environment_for_screen (CdkScreen *screen,
1683 char **envp)
1684{
1685 CdkDisplay *display;
1686 char **retval;
1687 char **freeme;
1688 char *display_name;
1689 int display_index = -1;
1690 int i, env_len;
1691
1692 g_return_val_if_fail (CDK_IS_SCREEN (screen), NULL)do { if (((((__extension__ ({ GTypeInstance *__inst = (GTypeInstance
*) ((screen)); GType __t = ((cdk_screen_get_type ())); gboolean
__r; if (!__inst) __r = (0); else if (__inst->g_class &&
__inst->g_class->g_type == __t) __r = (!(0)); else __r
= g_type_check_instance_is_a (__inst, __t); __r; })))))) { }
else { g_return_if_fail_warning ("CafeDesktop", ((const char
*) (__func__)), "CDK_IS_SCREEN (screen)"); return (((void*)0)
); } } while (0)
;
1693
1694 retval = freeme = NULL((void*)0);
1695
1696 if (envp == NULL((void*)0)) {
1697 envp = freeme = g_listenv ();
1698 for (i = 0; envp [i]; i++) {
1699 char *name = envp[i];
1700
1701 envp[i] = g_strjoin ("=", name, g_getenv (name), NULL((void*)0));
1702 g_free (name);
1703 }
1704 }
1705
1706 for (env_len = 0; envp [env_len]; env_len++)
1707 if (strncmp (envp [env_len], "DISPLAY", strlen ("DISPLAY")) == 0)
1708 display_index = env_len;
1709
1710 retval = g_new (char *, env_len + 1)((char * *) g_malloc_n ((env_len + 1), sizeof (char *)));
1711 retval [env_len] = NULL((void*)0);
1712
1713 display = cdk_screen_get_display (screen);
1714 display_name = g_strdup (cdk_display_get_name (display))g_strdup_inline (cdk_display_get_name (display));
1715
1716 for (i = 0; i < env_len; i++)
1717 if (i == display_index)
1718 retval [i] = g_strconcat ("DISPLAY=", display_name, NULL((void*)0));
1719 else
1720 retval [i] = g_strdup (envp[i])g_strdup_inline (envp[i]);
1721
1722 g_assert (i == env_len)do { if (i == env_len) ; else g_assertion_message_expr ("CafeDesktop"
, "cafe-desktop-item.c", 1722, ((const char*) (__func__)), "i == env_len"
); } while (0)
;
1723
1724 g_free (display_name);
1725 g_strfreev (freeme);
1726
1727 return retval;
1728}
1729
1730static void
1731dummy_child_watch (GPid pid G_GNUC_UNUSED__attribute__ ((__unused__)),
1732 gint status G_GNUC_UNUSED__attribute__ ((__unused__)),
1733 gpointer user_data G_GNUC_UNUSED__attribute__ ((__unused__)))
1734{
1735 /* Nothing, this is just to ensure we don't double fork
1736 * and break pkexec:
1737 * https://bugzilla.gnome.org/show_bug.cgi?id=675789
1738 */
1739}
1740
1741static int
1742ditem_execute (const CafeDesktopItem *item,
1743 const char *exec,
1744 GList *file_list,
1745 CdkScreen *screen,
1746 int workspace,
1747 char **envp,
1748 gboolean launch_only_one,
1749 gboolean use_current_dir,
1750 gboolean append_uris,
1751 gboolean append_paths,
1752 gboolean do_not_reap_child,
1753 GError **error)
1754{
1755 char **free_me = NULL((void*)0);
1756 int i, ret;
1
'ret' declared without an initial value
1757 char **term_argv = NULL((void*)0);
1758 int term_argc = 0;
1759 GSList *args, *arg_ptr;
1760 AddedStatus added_status;
1761 const char *working_dir = NULL((void*)0);
1762 char **temp_argv = NULL((void*)0);
1763 int temp_argc = 0;
1764 char *uris, *temp;
1765 char *exec_locale;
1766 int launched = 0;
1767 GPid pid;
1768#ifdef HAVE_STARTUP_NOTIFICATION
1769 CdkDisplay *cdkdisplay;
1770 SnLauncherContext *sn_context;
1771 SnDisplay *sn_display;
1772 const char *startup_class;
1773#endif
1774
1775 g_return_val_if_fail (item, -1)do { if ((item)) { } else { g_return_if_fail_warning ("CafeDesktop"
, ((const char*) (__func__)), "item"); return (-1); } } while
(0)
;
2
Assuming 'item' is non-null
3
Taking true branch
4
Loop condition is false. Exiting loop
1776
1777 if (item->type == CAFE_DESKTOP_ITEM_TYPE_APPLICATION) {
5
Assuming field 'type' is not equal to CAFE_DESKTOP_ITEM_TYPE_APPLICATION
1778 working_dir = cafe_desktop_item_get_string (item, CAFE_DESKTOP_ITEM_PATH"Path");
1779 if (working_dir &&
1780 !g_file_test (working_dir, G_FILE_TEST_IS_DIR))
1781 working_dir = NULL((void*)0);
1782 }
1783
1784 if (working_dir
5.1
'working_dir' is equal to NULL
== NULL((void*)0) && !use_current_dir)
6
Assuming 'use_current_dir' is not equal to 0
7
Taking false branch
1785 working_dir = g_get_home_dir ();
1786
1787 if (cafe_desktop_item_get_boolean (item, CAFE_DESKTOP_ITEM_TERMINAL"Terminal")) {
8
Taking false branch
1788 const char *options =
1789 cafe_desktop_item_get_string (item, CAFE_DESKTOP_ITEM_TERMINAL_OPTIONS"TerminalOptions");
1790
1791 if (options != NULL((void*)0)) {
1792 g_shell_parse_argv (options,
1793 &term_argc,
1794 &term_argv,
1795 NULL((void*)0) /* error */);
1796 /* ignore errors */
1797 }
1798
1799 cafe_desktop_prepend_terminal_to_vector (&term_argc, &term_argv);
1800 }
1801
1802 args = make_args (file_list);
1803 arg_ptr = make_args (file_list);
1804
1805#ifdef HAVE_STARTUP_NOTIFICATION
1806 if (screen)
9
Assuming 'screen' is null
10
Taking false branch
1807 cdkdisplay = cdk_screen_get_display (screen);
1808 else
1809 cdkdisplay = cdk_display_get_default ();
1810
1811 sn_display = sn_display_new (CDK_DISPLAY_XDISPLAY (cdkdisplay)(cdk_x11_display_get_xdisplay (cdkdisplay)),
1812 sn_error_trap_push,
1813 sn_error_trap_pop);
1814
1815
1816 /* Only initiate notification if desktop file supports it.
1817 * (we could avoid setting up the SnLauncherContext if we aren't going
1818 * to initiate, but why bother)
1819 */
1820
1821 startup_class = cafe_desktop_item_get_string (item,
1822 "StartupWMClass");
1823 if (startup_class ||
11
Assuming 'startup_class' is null
12
Taking false branch
1824 cafe_desktop_item_get_boolean (item, "StartupNotify")) {
1825 const char *name;
1826 const char *icon;
1827
1828 sn_context = sn_launcher_context_new (sn_display,
1829 screen ? cdk_x11_screen_get_screen_number (screen) :
1830 DefaultScreen (CDK_DISPLAY_XDISPLAY (cdkdisplay))(((_XPrivDisplay)((cdk_x11_display_get_xdisplay (cdkdisplay))
))->default_screen)
);
1831
1832 name = cafe_desktop_item_get_localestring (item,
1833 CAFE_DESKTOP_ITEM_NAME"Name");
1834
1835 if (name == NULL((void*)0))
1836 name = cafe_desktop_item_get_localestring (item,
1837 CAFE_DESKTOP_ITEM_GENERIC_NAME"GenericName");
1838
1839 if (name != NULL((void*)0)) {
1840 char *description;
1841
1842 sn_launcher_context_set_name (sn_context, name);
1843
1844 description = g_strdup_printf (_("Starting %s")((char *) g_dgettext ("cafe-desktop", "Starting %s")), name);
1845
1846 sn_launcher_context_set_description (sn_context, description);
1847
1848 g_free (description);
1849 }
1850
1851 icon = cafe_desktop_item_get_string (item,
1852 CAFE_DESKTOP_ITEM_ICON"Icon");
1853
1854 if (icon != NULL((void*)0))
1855 sn_launcher_context_set_icon_name (sn_context, icon);
1856
1857 sn_launcher_context_set_workspace (sn_context, workspace);
1858
1859 if (startup_class != NULL((void*)0))
1860 sn_launcher_context_set_wmclass (sn_context,
1861 startup_class);
1862 } else {
1863 sn_context = NULL((void*)0);
1864 }
1865#endif
1866
1867 if (screen
12.1
'screen' is null
) {
13
Taking false branch
1868 envp = make_environment_for_screen (screen, envp);
1869 if (free_me)
1870 g_strfreev (free_me);
1871 free_me = envp;
1872 }
1873
1874 exec_locale = g_filename_from_utf8 (exec, -1, NULL((void*)0), NULL((void*)0), NULL((void*)0));
1875
1876 if (exec_locale == NULL((void*)0)) {
14
Assuming 'exec_locale' is not equal to NULL
15
Taking false branch
1877 exec_locale = g_strdup ("")g_strdup_inline ("");
1878 }
1879
1880 do {
30
Loop condition is false. Exiting loop
1881 char **real_argv;
1882 char *new_exec;
1883 GSList *vector_list;
1884
1885 added_status = ADDED_NONE;
1886 new_exec = expand_string (item,
1887 exec_locale,
1888 args, &arg_ptr, &added_status);
1889
1890 if (launched
15.1
'launched' is equal to 0
== 0 && added_status
15.2
'added_status' is equal to ADDED_NONE
== ADDED_NONE && append_uris) {
16
Assuming 'append_uris' is 0
1891 uris = stringify_uris (args);
1892 temp = g_strconcat (new_exec, " ", uris, NULL((void*)0));
1893 g_free (uris);
1894 g_free (new_exec);
1895 new_exec = temp;
1896 added_status = ADDED_ALL;
1897 }
1898
1899 /* append_uris and append_paths are mutually exlusive */
1900 if (launched
16.1
'launched' is equal to 0
== 0 && added_status
16.2
'added_status' is equal to ADDED_NONE
== ADDED_NONE && append_paths) {
17
Assuming 'append_paths' is 0
1901 uris = stringify_files (args);
1902 temp = g_strconcat (new_exec, " ", uris, NULL((void*)0));
1903 g_free (uris);
1904 g_free (new_exec);
1905 new_exec = temp;
1906 added_status = ADDED_ALL;
1907 }
1908
1909 if (launched
17.1
'launched' is <= 0
> 0 && added_status == ADDED_NONE) {
1910 g_free (new_exec);
1911 break;
1912 }
1913
1914 if ( ! g_shell_parse_argv (new_exec,
18
Assuming the condition is false
19
Taking false branch
1915 &temp_argc, &temp_argv, error)) {
1916 /* The error now comes from g_shell_parse_argv */
1917 g_free (new_exec);
1918 ret = -1;
1919 break;
1920 }
1921 g_free (new_exec);
1922
1923 vector_list = NULL((void*)0);
1924 for(i = 0; i < term_argc; i++)
20
Loop condition is false. Execution continues on line 1928
1925 vector_list = g_slist_append (vector_list,
1926 g_strdup (term_argv[i])g_strdup_inline (term_argv[i]));
1927
1928 for(i = 0; i < temp_argc; i++)
21
Assuming 'i' is >= 'temp_argc'
22
Loop condition is false. Execution continues on line 1932
1929 vector_list = g_slist_append (vector_list,
1930 g_strdup (temp_argv[i])g_strdup_inline (temp_argv[i]));
1931
1932 g_strfreev (temp_argv);
1933
1934 real_argv = list_to_vector (vector_list);
1935 g_slist_foreach (vector_list, (GFunc)g_free, NULL((void*)0));
1936 g_slist_free (vector_list);
1937
1938#ifdef HAVE_STARTUP_NOTIFICATION
1939 if (sn_context
22.1
'sn_context' is equal to NULL
!= NULL((void*)0) &&
1940 !sn_launcher_context_get_initiated (sn_context)) {
1941 guint32 launch_time;
1942
1943 /* This means that we always use the first real_argv[0]
1944 * we select for the "binary name", but it's probably
1945 * OK to do that. Binary name isn't super-important
1946 * anyway, and we can't initiate twice, and we
1947 * must initiate prior to fork/exec.
1948 */
1949
1950 sn_launcher_context_set_binary_name (sn_context,
1951 real_argv[0]);
1952
1953 if (item->launch_time > 0)
1954 launch_time = item->launch_time;
1955 else
1956 launch_time = cdk_x11_display_get_user_time (cdkdisplay);
1957
1958 sn_launcher_context_initiate (sn_context,
1959 g_get_prgname () ? g_get_prgname () : "unknown",
1960 real_argv[0],
1961 launch_time);
1962
1963 /* Don't allow accidental reuse of same timestamp */
1964 ((CafeDesktopItem *)item)->launch_time = 0;
1965
1966 envp = make_spawn_environment_for_sn_context (sn_context, envp);
1967 if (free_me)
1968 g_strfreev (free_me);
1969 free_me = envp;
1970 }
1971#endif
1972
1973
1974 if ( ! g_spawn_async (working_dir,
26
Assuming the condition is false
27
Taking false branch
1975 real_argv,
1976 envp,
1977 (do_not_reap_child ? G_SPAWN_DO_NOT_REAP_CHILD : 0) | G_SPAWN_SEARCH_PATH /* flags */,
23
Assuming 'do_not_reap_child' is 0
24
'?' condition is false
1978 NULL((void*)0), /* child_setup_func */
1979 NULL((void*)0), /* child_setup_func_data */
1980 (do_not_reap_child
24.1
'do_not_reap_child' is 0
? &pid : NULL((void*)0)) /* child_pid */,
25
'?' condition is false
1981 error)) {
1982 /* The error was set for us,
1983 * we just can't launch this thingie */
1984 ret = -1;
1985 g_strfreev (real_argv);
1986 break;
1987 } else if (do_not_reap_child
27.1
'do_not_reap_child' is 0
) {
28
Taking false branch
1988 g_child_watch_add (pid, dummy_child_watch, NULL((void*)0));
1989 }
1990
1991 launched ++;
1992
1993 g_strfreev (real_argv);
1994
1995 if (arg_ptr != NULL((void*)0))
29
Assuming 'arg_ptr' is equal to NULL
1996 arg_ptr = arg_ptr->next;
1997
1998 /* rinse, repeat until we run out of arguments (That
1999 * is if we were adding singles anyway) */
2000 } while (added_status
29.1
'added_status' is not equal to ADDED_SINGLE
== ADDED_SINGLE &&
2001 arg_ptr != NULL((void*)0) &&
2002 ! launch_only_one);
2003
2004 g_free (exec_locale);
2005#ifdef HAVE_STARTUP_NOTIFICATION
2006 if (sn_context
30.1
'sn_context' is equal to NULL
!= NULL((void*)0)) {
31
Taking false branch
2007 if (ret < 0)
2008 sn_launcher_context_complete (sn_context); /* end sequence */
2009 else
2010 add_startup_timeout (screen ? screen :
2011 cdk_display_get_default_screen (cdk_display_get_default ()),
2012 sn_context);
2013 sn_launcher_context_unref (sn_context);
2014 }
2015
2016 sn_display_unref (sn_display);
2017#endif /* HAVE_STARTUP_NOTIFICATION */
2018
2019 free_args (args);
2020
2021 if (term_argv
31.1
'term_argv' is null
)
32
Taking false branch
2022 g_strfreev (term_argv);
2023
2024 if (free_me
32.1
'free_me' is null
)
33
Taking false branch
2025 g_strfreev (free_me);
2026
2027 return ret;
34
Undefined or garbage value returned to caller
2028}
2029
2030/* strip any trailing &, return FALSE if bad things happen and
2031 we end up with an empty string */
2032static gboolean
2033strip_the_amp (char *exec)
2034{
2035 size_t exec_len;
2036
2037 g_strstrip (exec)g_strchomp (g_strchug (exec));
2038 if (*exec == '\0')
2039 return FALSE(0);
2040
2041 exec_len = strlen (exec);
2042 /* kill any trailing '&' */
2043 if (exec[exec_len-1] == '&') {
2044 exec[exec_len-1] = '\0';
2045 g_strchomp (exec);
2046 }
2047
2048 /* can't exactly launch an empty thing */
2049 if (*exec == '\0')
2050 return FALSE(0);
2051
2052 return TRUE(!(0));
2053}
2054
2055
2056static int
2057cafe_desktop_item_launch_on_screen_with_env (
2058 const CafeDesktopItem *item,
2059 GList *file_list,
2060 CafeDesktopItemLaunchFlags flags,
2061 CdkScreen *screen,
2062 int workspace,
2063 char **envp,
2064 GError **error)
2065{
2066 const char *exec;
2067 char *the_exec;
2068 int ret;
2069
2070 exec = cafe_desktop_item_get_string (item, CAFE_DESKTOP_ITEM_EXEC"Exec");
2071 /* This is a URL, so launch it as a url */
2072 if (item->type == CAFE_DESKTOP_ITEM_TYPE_LINK) {
2073 const char *url;
2074 gboolean retval;
2075
2076 url = cafe_desktop_item_get_string (item, CAFE_DESKTOP_ITEM_URL"URL");
2077 /* Cafe panel used to put this in Exec */
2078 if (!(url && url[0] != '\0'))
2079 url = exec;
2080
2081 if (!(url && url[0] != '\0')) {
2082 g_set_error (error,
2083 CAFE_DESKTOP_ITEM_ERRORcafe_desktop_item_error_quark (),
2084 CAFE_DESKTOP_ITEM_ERROR_NO_URL,
2085 _("No URL to launch")((char *) g_dgettext ("cafe-desktop", "No URL to launch")));
2086 return -1;
2087 }
2088
2089 retval = ctk_show_uri_on_window (NULL((void*)0),
2090 url,
2091 CDK_CURRENT_TIME0L,
2092 error);
2093 return retval ? 0 : -1;
2094 }
2095
2096 /* check the type, if there is one set */
2097 if (item->type != CAFE_DESKTOP_ITEM_TYPE_APPLICATION) {
2098 g_set_error (error,
2099 CAFE_DESKTOP_ITEM_ERRORcafe_desktop_item_error_quark (),
2100 CAFE_DESKTOP_ITEM_ERROR_NOT_LAUNCHABLE,
2101 _("Not a launchable item")((char *) g_dgettext ("cafe-desktop", "Not a launchable item"
))
);
2102 return -1;
2103 }
2104
2105
2106 if (exec == NULL((void*)0) ||
2107 exec[0] == '\0') {
2108 g_set_error (error,
2109 CAFE_DESKTOP_ITEM_ERRORcafe_desktop_item_error_quark (),
2110 CAFE_DESKTOP_ITEM_ERROR_NO_EXEC_STRING,
2111 _("No command (Exec) to launch")((char *) g_dgettext ("cafe-desktop", "No command (Exec) to launch"
))
);
2112 return -1;
2113 }
2114
2115
2116 /* make a new copy and get rid of spaces */
2117 the_exec = g_alloca (strlen (exec) + 1)__builtin_alloca (strlen (exec) + 1);
2118 g_strlcpy (the_exec, exec, strlen (exec) + 1);
2119
2120 if ( ! strip_the_amp (the_exec)) {
2121 g_set_error (error,
2122 CAFE_DESKTOP_ITEM_ERRORcafe_desktop_item_error_quark (),
2123 CAFE_DESKTOP_ITEM_ERROR_BAD_EXEC_STRING,
2124 _("Bad command (Exec) to launch")((char *) g_dgettext ("cafe-desktop", "Bad command (Exec) to launch"
))
);
2125 return -1;
2126 }
2127
2128 ret = ditem_execute (item, the_exec, file_list, screen, workspace, envp,
2129 (flags & CAFE_DESKTOP_ITEM_LAUNCH_ONLY_ONE),
2130 (flags & CAFE_DESKTOP_ITEM_LAUNCH_USE_CURRENT_DIR),
2131 (flags & CAFE_DESKTOP_ITEM_LAUNCH_APPEND_URIS),
2132 (flags & CAFE_DESKTOP_ITEM_LAUNCH_APPEND_PATHS),
2133 (flags & CAFE_DESKTOP_ITEM_LAUNCH_DO_NOT_REAP_CHILD),
2134 error);
2135
2136 return ret;
2137}
2138
2139/**
2140 * cafe_desktop_item_launch:
2141 * @item: A desktop item
2142 * @file_list: Files/URIs to launch this item with, can be %NULL
2143 * @flags: FIXME
2144 * @error: FIXME
2145 *
2146 * This function runs the program listed in the specified 'item',
2147 * optionally appending additional arguments to its command line. It uses
2148 * #g_shell_parse_argv to parse the the exec string into a vector which is
2149 * then passed to #g_spawn_async for execution. This can return all
2150 * the errors from CafeURL, #g_shell_parse_argv and #g_spawn_async,
2151 * in addition to it's own. The files are
2152 * only added if the entry defines one of the standard % strings in it's
2153 * Exec field.
2154 *
2155 * Returns: The the pid of the process spawned. If more then one
2156 * process was spawned the last pid is returned. On error -1
2157 * is returned and @error is set.
2158 */
2159int
2160cafe_desktop_item_launch (const CafeDesktopItem *item,
2161 GList *file_list,
2162 CafeDesktopItemLaunchFlags flags,
2163 GError **error)
2164{
2165 return cafe_desktop_item_launch_on_screen_with_env (
2166 item, file_list, flags, NULL((void*)0), -1, NULL((void*)0), error);
2167}
2168
2169/**
2170 * cafe_desktop_item_launch_with_env:
2171 * @item: A desktop item
2172 * @file_list: Files/URIs to launch this item with, can be %NULL
2173 * @flags: FIXME
2174 * @envp: child's environment, or %NULL to inherit parent's
2175 * @error: FIXME
2176 *
2177 * See cafe_desktop_item_launch for a full description. This function
2178 * additionally passes an environment vector for the child process
2179 * which is to be launched.
2180 *
2181 * Returns: The the pid of the process spawned. If more then one
2182 * process was spawned the last pid is returned. On error -1
2183 * is returned and @error is set.
2184 */
2185int
2186cafe_desktop_item_launch_with_env (const CafeDesktopItem *item,
2187 GList *file_list,
2188 CafeDesktopItemLaunchFlags flags,
2189 char **envp,
2190 GError **error)
2191{
2192 return cafe_desktop_item_launch_on_screen_with_env (
2193 item, file_list, flags,
2194 NULL((void*)0), -1, envp, error);
2195}
2196
2197/**
2198 * cafe_desktop_item_launch_on_screen:
2199 * @item: A desktop item
2200 * @file_list: Files/URIs to launch this item with, can be %NULL
2201 * @flags: FIXME
2202 * @screen: the %CdkScreen on which the application should be launched
2203 * @workspace: the workspace on which the app should be launched (-1 for current)
2204 * @error: FIXME
2205 *
2206 * See cafe_desktop_item_launch for a full description. This function
2207 * additionally attempts to launch the application on a given screen
2208 * and workspace.
2209 *
2210 * Returns: The the pid of the process spawned. If more then one
2211 * process was spawned the last pid is returned. On error -1
2212 * is returned and @error is set.
2213 */
2214int
2215cafe_desktop_item_launch_on_screen (const CafeDesktopItem *item,
2216 GList *file_list,
2217 CafeDesktopItemLaunchFlags flags,
2218 CdkScreen *screen,
2219 int workspace,
2220 GError **error)
2221{
2222 return cafe_desktop_item_launch_on_screen_with_env (
2223 item, file_list, flags,
2224 screen, workspace, NULL((void*)0), error);
2225}
2226
2227/**
2228 * cafe_desktop_item_drop_uri_list:
2229 * @item: A desktop item
2230 * @uri_list: text as gotten from a text/uri-list
2231 * @flags: FIXME
2232 * @error: FIXME
2233 *
2234 * A list of files or urls dropped onto an icon, the proper (Url or File)
2235 * exec is run you can pass directly string that you got as the
2236 * text/uri-list. This just parses the list and calls
2237 *
2238 * Returns: The value returned by #cafe_execute_async() upon execution of
2239 * the specified item or -1 on error. If multiple instances are run, the
2240 * return of the last one is returned.
2241 */
2242int
2243cafe_desktop_item_drop_uri_list (const CafeDesktopItem *item,
2244 const char *uri_list,
2245 CafeDesktopItemLaunchFlags flags,
2246 GError **error)
2247{
2248 return cafe_desktop_item_drop_uri_list_with_env (item, uri_list,
2249 flags, NULL((void*)0), error);
2250}
2251
2252/**
2253* cafe_desktop_item_drop_uri_list_with_env:
2254* @item: A desktop item
2255* @uri_list: text as gotten from a text/uri-list
2256* @flags: FIXME
2257* @envp: child's environment
2258* @error: FIXME
2259*
2260* See cafe_desktop_item_drop_uri_list for a full description. This function
2261* additionally passes an environment vector for the child process
2262* which is to be launched.
2263*
2264* Returns: The value returned by #cafe_execute_async() upon execution of
2265* the specified item or -1 on error. If multiple instances are run, the
2266* return of the last one is returned.
2267*/
2268int
2269cafe_desktop_item_drop_uri_list_with_env (const CafeDesktopItem *item,
2270 const char *uri_list,
2271 CafeDesktopItemLaunchFlags flags,
2272 char **envp,
2273 GError **error)
2274{
2275 int ret;
2276 char *uri;
2277 char **uris;
2278 GList *list = NULL((void*)0);
2279
2280 uris = g_uri_list_extract_uris (uri_list);
2281
2282 for (uri = uris[0]; uri != NULL((void*)0); uri++) {
2283 list = g_list_prepend (list, uri);
2284 }
2285 list = g_list_reverse (list);
2286
2287 ret = cafe_desktop_item_launch_with_env (
2288 item, list, flags, envp, error);
2289
2290 g_strfreev (uris);
2291 g_list_free (list);
2292
2293 return ret;
2294}
2295
2296static gboolean
2297exec_exists (const char *exec)
2298{
2299 if (g_path_is_absolute (exec)) {
2300 if (access (exec, X_OK1) == 0)
2301 return TRUE(!(0));
2302 else
2303 return FALSE(0);
2304 } else {
2305 char *tryme;
2306
2307 tryme = g_find_program_in_path (exec);
2308 if (tryme != NULL((void*)0)) {
2309 g_free (tryme);
2310 return TRUE(!(0));
2311 }
2312 return FALSE(0);
2313 }
2314}
2315
2316/**
2317 * cafe_desktop_item_exists:
2318 * @item: A desktop item
2319 *
2320 * Attempt to figure out if the program that can be executed by this item
2321 * actually exists. First it tries the TryExec attribute to see if that
2322 * contains a program that is in the path. Then if there is no such
2323 * attribute, it tries the first word of the Exec attribute.
2324 *
2325 * Returns: A boolean, %TRUE if it exists, %FALSE otherwise.
2326 */
2327gboolean
2328cafe_desktop_item_exists (const CafeDesktopItem *item)
2329{
2330 const char *try_exec;
2331
2332 g_return_val_if_fail (item != NULL, FALSE)do { if ((item != ((void*)0))) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "item != NULL");
return ((0)); } } while (0)
;
2333
2334 try_exec = lookup (item, CAFE_DESKTOP_ITEM_TRY_EXEC"TryExec");
2335
2336 if (try_exec != NULL((void*)0) &&
2337 ! exec_exists (try_exec)) {
2338 return FALSE(0);
2339 }
2340
2341 if (item->type == CAFE_DESKTOP_ITEM_TYPE_APPLICATION) {
2342 int argc;
2343 char **argv;
2344 const char *exe;
2345 const char *exec;
2346
2347 exec = lookup (item, CAFE_DESKTOP_ITEM_EXEC"Exec");
2348 if (exec == NULL((void*)0))
2349 return FALSE(0);
2350
2351 if ( ! g_shell_parse_argv (exec, &argc, &argv, NULL((void*)0)))
2352 return FALSE(0);
2353
2354 if (argc < 1) {
2355 g_strfreev (argv);
2356 return FALSE(0);
2357 }
2358
2359 exe = argv[0];
2360
2361 if ( ! exec_exists (exe)) {
2362 g_strfreev (argv);
2363 return FALSE(0);
2364 }
2365 g_strfreev (argv);
2366 }
2367
2368 return TRUE(!(0));
2369}
2370
2371/**
2372 * cafe_desktop_item_get_entry_type:
2373 * @item: A desktop item
2374 *
2375 * Gets the type attribute (the 'Type' field) of the item. This should
2376 * usually be 'Application' for an application, but it can be 'Directory'
2377 * for a directory description. There are other types available as well.
2378 * The type usually indicates how the desktop item should be handeled and
2379 * how the 'Exec' field should be handeled.
2380 *
2381 * Returns: The type of the specified 'item'. The returned
2382 * memory remains owned by the CafeDesktopItem and should not be freed.
2383 */
2384CafeDesktopItemType
2385cafe_desktop_item_get_entry_type (const CafeDesktopItem *item)
2386{
2387 g_return_val_if_fail (item != NULL, 0)do { if ((item != ((void*)0))) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "item != NULL");
return (0); } } while (0)
;
2388 g_return_val_if_fail (item->refcount > 0, 0)do { if ((item->refcount > 0)) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "item->refcount > 0"
); return (0); } } while (0)
;
2389
2390 return item->type;
2391}
2392
2393void
2394cafe_desktop_item_set_entry_type (CafeDesktopItem *item,
2395 CafeDesktopItemType type)
2396{
2397 g_return_if_fail (item != NULL)do { if ((item != ((void*)0))) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "item != NULL");
return; } } while (0)
;
2398 g_return_if_fail (item->refcount > 0)do { if ((item->refcount > 0)) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "item->refcount > 0"
); return; } } while (0)
;
2399
2400 item->type = type;
2401
2402 switch (type) {
2403 case CAFE_DESKTOP_ITEM_TYPE_NULL:
2404 set (item, CAFE_DESKTOP_ITEM_TYPE"Type", NULL((void*)0));
2405 break;
2406 case CAFE_DESKTOP_ITEM_TYPE_APPLICATION:
2407 set (item, CAFE_DESKTOP_ITEM_TYPE"Type", "Application");
2408 break;
2409 case CAFE_DESKTOP_ITEM_TYPE_LINK:
2410 set (item, CAFE_DESKTOP_ITEM_TYPE"Type", "Link");
2411 break;
2412 case CAFE_DESKTOP_ITEM_TYPE_FSDEVICE:
2413 set (item, CAFE_DESKTOP_ITEM_TYPE"Type", "FSDevice");
2414 break;
2415 case CAFE_DESKTOP_ITEM_TYPE_MIME_TYPE:
2416 set (item, CAFE_DESKTOP_ITEM_TYPE"Type", "MimeType");
2417 break;
2418 case CAFE_DESKTOP_ITEM_TYPE_DIRECTORY:
2419 set (item, CAFE_DESKTOP_ITEM_TYPE"Type", "Directory");
2420 break;
2421 case CAFE_DESKTOP_ITEM_TYPE_SERVICE:
2422 set (item, CAFE_DESKTOP_ITEM_TYPE"Type", "Service");
2423 break;
2424 case CAFE_DESKTOP_ITEM_TYPE_SERVICE_TYPE:
2425 set (item, CAFE_DESKTOP_ITEM_TYPE"Type", "ServiceType");
2426 break;
2427 default:
2428 break;
2429 }
2430}
2431
2432
2433
2434/**
2435 * cafe_desktop_item_get_file_status:
2436 * @item: A desktop item
2437 *
2438 * This function checks the modification time of the on-disk file to
2439 * see if it is more recent than the in-memory data.
2440 *
2441 * Returns: An enum value that specifies whether the item has changed since being loaded.
2442 */
2443CafeDesktopItemStatus
2444cafe_desktop_item_get_file_status (const CafeDesktopItem *item)
2445{
2446 CafeDesktopItemStatus retval;
2447 GFile *file;
2448 GFileInfo *info;
2449
2450 g_return_val_if_fail (item != NULL, CAFE_DESKTOP_ITEM_DISAPPEARED)do { if ((item != ((void*)0))) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "item != NULL");
return (CAFE_DESKTOP_ITEM_DISAPPEARED); } } while (0)
;
2451 g_return_val_if_fail (item->refcount > 0, CAFE_DESKTOP_ITEM_DISAPPEARED)do { if ((item->refcount > 0)) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "item->refcount > 0"
); return (CAFE_DESKTOP_ITEM_DISAPPEARED); } } while (0)
;
2452
2453 if (item->location == NULL((void*)0))
2454 return CAFE_DESKTOP_ITEM_DISAPPEARED;
2455
2456 file = g_file_new_for_uri (item->location);
2457 info = g_file_query_info (file, G_FILE_ATTRIBUTE_TIME_MODIFIED"time::modified",
2458 G_FILE_QUERY_INFO_NONE, NULL((void*)0), NULL((void*)0));
2459
2460 retval = CAFE_DESKTOP_ITEM_UNCHANGED;
2461
2462 if (!g_file_info_has_attribute (info, G_FILE_ATTRIBUTE_TIME_MODIFIED"time::modified"))
2463 retval = CAFE_DESKTOP_ITEM_DISAPPEARED;
2464 else if (item->mtime < g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_MODIFIED"time::modified"))
2465 retval = CAFE_DESKTOP_ITEM_CHANGED;
2466
2467 g_object_unref (info);
2468 g_object_unref (file);
2469
2470 return retval;
2471}
2472
2473/**
2474 * cafe_desktop_item_find_icon:
2475 * @icon_theme: a #CtkIconTheme
2476 * @icon: icon name, something you'd get out of the Icon key
2477 * @desired_size: FIXME
2478 * @flags: FIXME
2479 *
2480 * Description: This function goes and looks for the icon file. If the icon
2481 * is not an absolute filename, this will look for it in the standard places.
2482 * If it can't find the icon, it will return %NULL
2483 *
2484 * Returns: A newly allocated string
2485 */
2486char *
2487cafe_desktop_item_find_icon (CtkIconTheme *icon_theme,
2488 const char *icon,
2489 int desired_size,
2490 int flags G_GNUC_UNUSED__attribute__ ((__unused__)))
2491{
2492 CtkIconInfo *info;
2493 char *full = NULL((void*)0);
2494
2495 g_return_val_if_fail (icon_theme == NULL ||do { if ((icon_theme == ((void*)0) || (((__extension__ ({ GTypeInstance
*__inst = (GTypeInstance*) ((icon_theme)); GType __t = ((ctk_icon_theme_get_type
())); gboolean __r; if (!__inst) __r = (0); else if (__inst->
g_class && __inst->g_class->g_type == __t) __r =
(!(0)); else __r = g_type_check_instance_is_a (__inst, __t);
__r; })))))) { } else { g_return_if_fail_warning ("CafeDesktop"
, ((const char*) (__func__)), "icon_theme == NULL || CTK_IS_ICON_THEME (icon_theme)"
); return (((void*)0)); } } while (0)
2496 CTK_IS_ICON_THEME (icon_theme), NULL)do { if ((icon_theme == ((void*)0) || (((__extension__ ({ GTypeInstance
*__inst = (GTypeInstance*) ((icon_theme)); GType __t = ((ctk_icon_theme_get_type
())); gboolean __r; if (!__inst) __r = (0); else if (__inst->
g_class && __inst->g_class->g_type == __t) __r =
(!(0)); else __r = g_type_check_instance_is_a (__inst, __t);
__r; })))))) { } else { g_return_if_fail_warning ("CafeDesktop"
, ((const char*) (__func__)), "icon_theme == NULL || CTK_IS_ICON_THEME (icon_theme)"
); return (((void*)0)); } } while (0)
;
2497
2498 if (icon == NULL((void*)0) || strcmp(icon,"") == 0) {
2499 return NULL((void*)0);
2500 } else if (g_path_is_absolute (icon)) {
2501 if (g_file_test (icon, G_FILE_TEST_EXISTS)) {
2502 return g_strdup (icon)g_strdup_inline (icon);
2503 } else {
2504 return NULL((void*)0);
2505 }
2506 } else {
2507 char *icon_no_extension;
2508 char *p;
2509
2510 if (icon_theme == NULL((void*)0))
2511 icon_theme = ctk_icon_theme_get_default ();
2512
2513 icon_no_extension = g_strdup (icon)g_strdup_inline (icon);
2514 p = strrchr (icon_no_extension, '.');
2515 if (p &&
2516 (strcmp (p, ".png") == 0 ||
2517 strcmp (p, ".xpm") == 0 ||
2518 strcmp (p, ".svg") == 0)) {
2519 *p = 0;
2520 }
2521
2522
2523 info = ctk_icon_theme_lookup_icon (icon_theme,
2524 icon_no_extension,
2525 desired_size,
2526 0);
2527
2528 full = NULL((void*)0);
2529 if (info) {
2530 full = g_strdup (ctk_icon_info_get_filename (info))g_strdup_inline (ctk_icon_info_get_filename (info));
2531 g_object_unref (info);
2532 }
2533 g_free (icon_no_extension);
2534 }
2535
2536 return full;
2537
2538}
2539
2540/**
2541 * cafe_desktop_item_get_icon:
2542 * @icon_theme: a #CtkIconTheme
2543 * @item: A desktop item
2544 *
2545 * Description: This function goes and looks for the icon file. If the icon
2546 * is not set as an absolute filename, this will look for it in the standard places.
2547 * If it can't find the icon, it will return %NULL
2548 *
2549 * Returns: A newly allocated string
2550 */
2551char *
2552cafe_desktop_item_get_icon (const CafeDesktopItem *item,
2553 CtkIconTheme *icon_theme)
2554{
2555 /* maybe this function should be deprecated in favour of find icon
2556 * -George */
2557 const char *icon;
2558
2559 g_return_val_if_fail (item != NULL, NULL)do { if ((item != ((void*)0))) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "item != NULL");
return (((void*)0)); } } while (0)
;
2560 g_return_val_if_fail (item->refcount > 0, NULL)do { if ((item->refcount > 0)) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "item->refcount > 0"
); return (((void*)0)); } } while (0)
;
2561
2562 icon = cafe_desktop_item_get_string (item, CAFE_DESKTOP_ITEM_ICON"Icon");
2563
2564 return cafe_desktop_item_find_icon (icon_theme, icon,
2565 48 /* desired_size */,
2566 0 /* flags */);
2567}
2568
2569/**
2570 * cafe_desktop_item_get_location:
2571 * @item: A desktop item
2572 *
2573 * Returns: The file location associated with 'item'.
2574 *
2575 */
2576const char *
2577cafe_desktop_item_get_location (const CafeDesktopItem *item)
2578{
2579 g_return_val_if_fail (item != NULL, NULL)do { if ((item != ((void*)0))) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "item != NULL");
return (((void*)0)); } } while (0)
;
2580 g_return_val_if_fail (item->refcount > 0, NULL)do { if ((item->refcount > 0)) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "item->refcount > 0"
); return (((void*)0)); } } while (0)
;
2581
2582 return item->location;
2583}
2584
2585/**
2586 * cafe_desktop_item_set_location:
2587 * @item: A desktop item
2588 * @location: A uri string specifying the file location of this particular item.
2589 *
2590 * Set's the 'location' uri of this item.
2591 */
2592void
2593cafe_desktop_item_set_location (CafeDesktopItem *item, const char *location)
2594{
2595 g_return_if_fail (item != NULL)do { if ((item != ((void*)0))) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "item != NULL");
return; } } while (0)
;
2596 g_return_if_fail (item->refcount > 0)do { if ((item->refcount > 0)) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "item->refcount > 0"
); return; } } while (0)
;
2597
2598 if (item->location != NULL((void*)0) &&
2599 location != NULL((void*)0) &&
2600 strcmp (item->location, location) == 0)
2601 return;
2602
2603 g_free (item->location);
2604 item->location = g_strdup (location)g_strdup_inline (location);
2605
2606 /* This is ugly, but useful internally */
2607 if (item->mtime != DONT_UPDATE_MTIME((time_t)-2)) {
2608 item->mtime = 0;
2609
2610 if (item->location) {
2611 GFile *file;
2612 GFileInfo *info;
2613
2614 file = g_file_new_for_uri (item->location);
2615
2616 info = g_file_query_info (file,
2617 G_FILE_ATTRIBUTE_TIME_MODIFIED"time::modified",
2618 G_FILE_QUERY_INFO_NONE,
2619 NULL((void*)0), NULL((void*)0));
2620 if (info) {
2621 if (g_file_info_has_attribute (info,
2622 G_FILE_ATTRIBUTE_TIME_MODIFIED"time::modified"))
2623 item->mtime = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_MODIFIED"time::modified");
2624 g_object_unref (info);
2625 }
2626
2627 g_object_unref (file);
2628 }
2629 }
2630
2631 /* Make sure that save actually saves */
2632 item->modified = TRUE(!(0));
2633}
2634
2635/**
2636 * cafe_desktop_item_set_location_file:
2637 * @item: A desktop item
2638 * @file: A local filename specifying the file location of this particular item.
2639 *
2640 * Set's the 'location' uri of this item to the given @file.
2641 */
2642void
2643cafe_desktop_item_set_location_file (CafeDesktopItem *item, const char *file)
2644{
2645 g_return_if_fail (item != NULL)do { if ((item != ((void*)0))) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "item != NULL");
return; } } while (0)
;
2646 g_return_if_fail (item->refcount > 0)do { if ((item->refcount > 0)) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "item->refcount > 0"
); return; } } while (0)
;
2647
2648 if (file != NULL((void*)0)) {
2649 GFile *gfile;
2650
2651 gfile = g_file_new_for_path (file);
2652 cafe_desktop_item_set_location_gfile (item, gfile);
2653 g_object_unref (gfile);
2654 } else {
2655 cafe_desktop_item_set_location (item, NULL((void*)0));
2656 }
2657}
2658
2659static void
2660cafe_desktop_item_set_location_gfile (CafeDesktopItem *item, GFile *file)
2661{
2662 g_return_if_fail (item != NULL)do { if ((item != ((void*)0))) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "item != NULL");
return; } } while (0)
;
2663 g_return_if_fail (item->refcount > 0)do { if ((item->refcount > 0)) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "item->refcount > 0"
); return; } } while (0)
;
2664
2665 if (file != NULL((void*)0)) {
2666 char *uri;
2667
2668 uri = g_file_get_uri (file);
2669 cafe_desktop_item_set_location (item, uri);
2670 g_free (uri);
2671 } else {
2672 cafe_desktop_item_set_location (item, NULL((void*)0));
2673 }
2674}
2675
2676/*
2677 * Reading/Writing different sections, NULL is the standard section
2678 */
2679
2680gboolean
2681cafe_desktop_item_attr_exists (const CafeDesktopItem *item,
2682 const char *attr)
2683{
2684 g_return_val_if_fail (item != NULL, FALSE)do { if ((item != ((void*)0))) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "item != NULL");
return ((0)); } } while (0)
;
2685 g_return_val_if_fail (item->refcount > 0, FALSE)do { if ((item->refcount > 0)) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "item->refcount > 0"
); return ((0)); } } while (0)
;
2686 g_return_val_if_fail (attr != NULL, FALSE)do { if ((attr != ((void*)0))) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "attr != NULL");
return ((0)); } } while (0)
;
2687
2688 return lookup (item, attr) != NULL((void*)0);
2689}
2690
2691/*
2692 * String type
2693 */
2694const char *
2695cafe_desktop_item_get_string (const CafeDesktopItem *item,
2696 const char *attr)
2697{
2698 g_return_val_if_fail (item != NULL, NULL)do { if ((item != ((void*)0))) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "item != NULL");
return (((void*)0)); } } while (0)
;
2699 g_return_val_if_fail (item->refcount > 0, NULL)do { if ((item->refcount > 0)) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "item->refcount > 0"
); return (((void*)0)); } } while (0)
;
2700 g_return_val_if_fail (attr != NULL, NULL)do { if ((attr != ((void*)0))) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "attr != NULL");
return (((void*)0)); } } while (0)
;
2701
2702 return lookup (item, attr);
2703}
2704
2705void
2706cafe_desktop_item_set_string (CafeDesktopItem *item,
2707 const char *attr,
2708 const char *value)
2709{
2710 g_return_if_fail (item != NULL)do { if ((item != ((void*)0))) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "item != NULL");
return; } } while (0)
;
2711 g_return_if_fail (item->refcount > 0)do { if ((item->refcount > 0)) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "item->refcount > 0"
); return; } } while (0)
;
2712 g_return_if_fail (attr != NULL)do { if ((attr != ((void*)0))) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "attr != NULL");
return; } } while (0)
;
2713
2714 set (item, attr, value);
2715
2716 if (strcmp (attr, CAFE_DESKTOP_ITEM_TYPE"Type") == 0)
2717 item->type = type_from_string (value);
2718}
2719
2720/*
2721 * LocaleString type
2722 */
2723const char* cafe_desktop_item_get_localestring(const CafeDesktopItem* item, const char* attr)
2724{
2725 g_return_val_if_fail(item != NULL, NULL)do { if ((item != ((void*)0))) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "item != NULL");
return (((void*)0)); } } while (0)
;
2726 g_return_val_if_fail(item->refcount > 0, NULL)do { if ((item->refcount > 0)) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "item->refcount > 0"
); return (((void*)0)); } } while (0)
;
2727 g_return_val_if_fail(attr != NULL, NULL)do { if ((attr != ((void*)0))) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "attr != NULL");
return (((void*)0)); } } while (0)
;
2728
2729 return lookup_best_locale(item, attr);
2730}
2731
2732const char* cafe_desktop_item_get_localestring_lang(const CafeDesktopItem* item, const char* attr, const char* language)
2733{
2734 g_return_val_if_fail(item != NULL, NULL)do { if ((item != ((void*)0))) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "item != NULL");
return (((void*)0)); } } while (0)
;
2735 g_return_val_if_fail(item->refcount > 0, NULL)do { if ((item->refcount > 0)) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "item->refcount > 0"
); return (((void*)0)); } } while (0)
;
2736 g_return_val_if_fail(attr != NULL, NULL)do { if ((attr != ((void*)0))) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "attr != NULL");
return (((void*)0)); } } while (0)
;
2737
2738 return lookup_locale(item, attr, language);
2739}
2740
2741/**
2742 * cafe_desktop_item_get_string_locale:
2743 * @item: A desktop item
2744 * @attr: An attribute name
2745 *
2746 * Returns the current locale that is used for the given attribute.
2747 * This might not be the same for all attributes. For example, if your
2748 * locale is "en_US.ISO8859-1" but attribute FOO only has "en_US" then
2749 * that would be returned for attr = "FOO". If attribute BAR has
2750 * "en_US.ISO8859-1" then that would be returned for "BAR".
2751 *
2752 * Returns: a string equal to the current locale or NULL
2753 * if the attribute is invalid or there is no matching locale.
2754 */
2755const char *
2756cafe_desktop_item_get_attr_locale (const CafeDesktopItem *item,
2757 const char *attr)
2758{
2759 const char * const *langs_pointer;
2760 int i;
2761
2762 langs_pointer = g_get_language_names ();
2763 for (i = 0; langs_pointer[i] != NULL((void*)0); i++) {
2764 const char *value = NULL((void*)0);
2765
2766 value = lookup_locale (item, attr, langs_pointer[i]);
2767 if (value)
2768 return langs_pointer[i];
2769 }
2770
2771 return NULL((void*)0);
2772}
2773
2774GList *
2775cafe_desktop_item_get_languages (const CafeDesktopItem *item,
2776 const char *attr)
2777{
2778 GList *li;
2779 GList *list = NULL((void*)0);
2780
2781 g_return_val_if_fail (item != NULL, NULL)do { if ((item != ((void*)0))) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "item != NULL");
return (((void*)0)); } } while (0)
;
2782 g_return_val_if_fail (item->refcount > 0, NULL)do { if ((item->refcount > 0)) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "item->refcount > 0"
); return (((void*)0)); } } while (0)
;
2783
2784 for (li = item->languages; li != NULL((void*)0); li = li->next) {
2785 char *language = li->data;
2786 if (attr == NULL((void*)0) ||
2787 lookup_locale (item, attr, language) != NULL((void*)0)) {
2788 list = g_list_prepend (list, language);
2789 }
2790 }
2791
2792 return g_list_reverse (list);
2793}
2794
2795static const char *
2796get_language (void)
2797{
2798 const char * const *langs_pointer;
2799 int i;
2800
2801 langs_pointer = g_get_language_names ();
2802 for (i = 0; langs_pointer[i] != NULL((void*)0); i++) {
2803 /* find first without encoding */
2804 if (strchr (langs_pointer[i], '.') == NULL((void*)0)) {
2805 return langs_pointer[i];
2806 }
2807 }
2808 return NULL((void*)0);
2809}
2810
2811void
2812cafe_desktop_item_set_localestring (CafeDesktopItem *item,
2813 const char *attr,
2814 const char *value)
2815{
2816 g_return_if_fail (item != NULL)do { if ((item != ((void*)0))) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "item != NULL");
return; } } while (0)
;
2817 g_return_if_fail (item->refcount > 0)do { if ((item->refcount > 0)) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "item->refcount > 0"
); return; } } while (0)
;
2818 g_return_if_fail (attr != NULL)do { if ((attr != ((void*)0))) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "attr != NULL");
return; } } while (0)
;
2819
2820 set_locale (item, attr, get_language (), value);
2821}
2822
2823void
2824cafe_desktop_item_set_localestring_lang (CafeDesktopItem *item,
2825 const char *attr,
2826 const char *language,
2827 const char *value)
2828{
2829 g_return_if_fail (item != NULL)do { if ((item != ((void*)0))) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "item != NULL");
return; } } while (0)
;
2830 g_return_if_fail (item->refcount > 0)do { if ((item->refcount > 0)) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "item->refcount > 0"
); return; } } while (0)
;
2831 g_return_if_fail (attr != NULL)do { if ((attr != ((void*)0))) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "attr != NULL");
return; } } while (0)
;
2832
2833 set_locale (item, attr, language, value);
2834}
2835
2836void
2837cafe_desktop_item_clear_localestring (CafeDesktopItem *item,
2838 const char *attr)
2839{
2840 GList *l;
2841
2842 g_return_if_fail (item != NULL)do { if ((item != ((void*)0))) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "item != NULL");
return; } } while (0)
;
2843 g_return_if_fail (item->refcount > 0)do { if ((item->refcount > 0)) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "item->refcount > 0"
); return; } } while (0)
;
2844 g_return_if_fail (attr != NULL)do { if ((attr != ((void*)0))) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "attr != NULL");
return; } } while (0)
;
2845
2846 for (l = item->languages; l != NULL((void*)0); l = l->next)
2847 set_locale (item, attr, l->data, NULL((void*)0));
2848
2849 set (item, attr, NULL((void*)0));
2850}
2851
2852/*
2853 * Strings, Regexps types
2854 */
2855
2856char **
2857cafe_desktop_item_get_strings (const CafeDesktopItem *item,
2858 const char *attr)
2859{
2860 const char *value;
2861
2862 g_return_val_if_fail (item != NULL, NULL)do { if ((item != ((void*)0))) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "item != NULL");
return (((void*)0)); } } while (0)
;
2863 g_return_val_if_fail (item->refcount > 0, NULL)do { if ((item->refcount > 0)) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "item->refcount > 0"
); return (((void*)0)); } } while (0)
;
2864 g_return_val_if_fail (attr != NULL, NULL)do { if ((attr != ((void*)0))) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "attr != NULL");
return (((void*)0)); } } while (0)
;
2865
2866 value = lookup (item, attr);
2867 if (value == NULL((void*)0))
2868 return NULL((void*)0);
2869
2870 /* FIXME: there's no way to escape semicolons apparently */
2871 return g_strsplit (value, ";", -1);
2872}
2873
2874void
2875cafe_desktop_item_set_strings (CafeDesktopItem *item,
2876 const char *attr,
2877 char **strings)
2878{
2879 char *str, *str2;
2880
2881 g_return_if_fail (item != NULL)do { if ((item != ((void*)0))) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "item != NULL");
return; } } while (0)
;
2882 g_return_if_fail (item->refcount > 0)do { if ((item->refcount > 0)) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "item->refcount > 0"
); return; } } while (0)
;
2883 g_return_if_fail (attr != NULL)do { if ((attr != ((void*)0))) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "attr != NULL");
return; } } while (0)
;
2884
2885 str = g_strjoinv (";", strings);
2886 str2 = g_strconcat (str, ";", NULL((void*)0));
2887 /* FIXME: there's no way to escape semicolons apparently */
2888 set (item, attr, str2);
2889 g_free (str);
2890 g_free (str2);
2891}
2892
2893/*
2894 * Boolean type
2895 */
2896gboolean
2897cafe_desktop_item_get_boolean (const CafeDesktopItem *item,
2898 const char *attr)
2899{
2900 const char *value;
2901
2902 g_return_val_if_fail (item != NULL, FALSE)do { if ((item != ((void*)0))) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "item != NULL");
return ((0)); } } while (0)
;
2903 g_return_val_if_fail (item->refcount > 0, FALSE)do { if ((item->refcount > 0)) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "item->refcount > 0"
); return ((0)); } } while (0)
;
2904 g_return_val_if_fail (attr != NULL, FALSE)do { if ((attr != ((void*)0))) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "attr != NULL");
return ((0)); } } while (0)
;
2905
2906 value = lookup (item, attr);
2907 if (value == NULL((void*)0))
2908 return FALSE(0);
2909
2910 return (value[0] == 'T' ||
2911 value[0] == 't' ||
2912 value[0] == 'Y' ||
2913 value[0] == 'y' ||
2914 atoi (value) != 0);
2915}
2916
2917void
2918cafe_desktop_item_set_boolean (CafeDesktopItem *item,
2919 const char *attr,
2920 gboolean value)
2921{
2922 g_return_if_fail (item != NULL)do { if ((item != ((void*)0))) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "item != NULL");
return; } } while (0)
;
2923 g_return_if_fail (item->refcount > 0)do { if ((item->refcount > 0)) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "item->refcount > 0"
); return; } } while (0)
;
2924 g_return_if_fail (attr != NULL)do { if ((attr != ((void*)0))) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "attr != NULL");
return; } } while (0)
;
2925
2926 set (item, attr, value ? "true" : "false");
2927}
2928
2929void
2930cafe_desktop_item_set_launch_time (CafeDesktopItem *item,
2931 guint32 timestamp)
2932{
2933 g_return_if_fail (item != NULL)do { if ((item != ((void*)0))) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "item != NULL");
return; } } while (0)
;
2934
2935 item->launch_time = timestamp;
2936}
2937
2938/*
2939 * Clearing attributes
2940 */
2941void
2942cafe_desktop_item_clear_section (CafeDesktopItem *item,
2943 const char *section)
2944{
2945 Section *sec;
2946 GList *li;
2947
2948 g_return_if_fail (item != NULL)do { if ((item != ((void*)0))) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "item != NULL");
return; } } while (0)
;
2949 g_return_if_fail (item->refcount > 0)do { if ((item->refcount > 0)) { } else { g_return_if_fail_warning
("CafeDesktop", ((const char*) (__func__)), "item->refcount > 0"
); return; } } while (0)
;
2950
2951 sec = find_section (item, section);
2952
2953 if (sec == NULL((void*)0)) {
2954 for (li = item->keys; li != NULL((void*)0); li = li->next) {
2955 g_hash_table_remove (item->main_hash, li->data);
2956 g_free (li->data);
2957 li->data = NULL((void*)0);
2958 }
2959 g_list_free (item->keys);
2960 item->keys = NULL((void*)0);
2961 } else {
2962 for (li = sec->keys; li != NULL((void*)0); li = li->next) {
2963 char *key = li->data;
2964 char *full = g_strdup_printf ("%s/%s",
2965 sec->name, key);
2966 g_hash_table_remove (item->main_hash, full);
2967 g_free (full);
2968 g_free (key);
2969 li->data = NULL((void*)0);
2970 }
2971 g_list_free (sec->keys);
2972 sec->keys = NULL((void*)0);
2973 }
2974 item->modified = TRUE(!(0));
2975}
2976
2977/************************************************************
2978 * Parser: *
2979 ************************************************************/
2980
2981static gboolean G_GNUC_CONST__attribute__ ((__const__))
2982standard_is_boolean (const char * key)
2983{
2984 static GHashTable *bools = NULL((void*)0);
2985
2986 if (bools == NULL((void*)0)) {
2987 bools = g_hash_table_new (g_str_hash, g_str_equal);
2988 g_hash_table_insert (bools,
2989 CAFE_DESKTOP_ITEM_NO_DISPLAY"NoDisplay",
2990 CAFE_DESKTOP_ITEM_NO_DISPLAY"NoDisplay");
2991 g_hash_table_insert (bools,
2992 CAFE_DESKTOP_ITEM_HIDDEN"Hidden",
2993 CAFE_DESKTOP_ITEM_HIDDEN"Hidden");
2994 g_hash_table_insert (bools,
2995 CAFE_DESKTOP_ITEM_TERMINAL"Terminal",
2996 CAFE_DESKTOP_ITEM_TERMINAL"Terminal");
2997 g_hash_table_insert (bools,
2998 CAFE_DESKTOP_ITEM_READ_ONLY"ReadOnly",
2999 CAFE_DESKTOP_ITEM_READ_ONLY"ReadOnly");
3000 }
3001
3002 return g_hash_table_lookup (bools, key) != NULL((void*)0);
3003}
3004
3005static gboolean G_GNUC_CONST__attribute__ ((__const__))
3006standard_is_strings (const char *key)
3007{
3008 static GHashTable *strings = NULL((void*)0);
3009
3010 if (strings == NULL((void*)0)) {
3011 strings = g_hash_table_new (g_str_hash, g_str_equal);
3012 g_hash_table_insert (strings,
3013 CAFE_DESKTOP_ITEM_FILE_PATTERN"FilePattern",
3014 CAFE_DESKTOP_ITEM_FILE_PATTERN"FilePattern");
3015 g_hash_table_insert (strings,
3016 CAFE_DESKTOP_ITEM_ACTIONS"Actions",
3017 CAFE_DESKTOP_ITEM_ACTIONS"Actions");
3018 g_hash_table_insert (strings,
3019 CAFE_DESKTOP_ITEM_MIME_TYPE"MimeType",
3020 CAFE_DESKTOP_ITEM_MIME_TYPE"MimeType");
3021 g_hash_table_insert (strings,
3022 CAFE_DESKTOP_ITEM_PATTERNS"Patterns",
3023 CAFE_DESKTOP_ITEM_PATTERNS"Patterns");
3024 g_hash_table_insert (strings,
3025 CAFE_DESKTOP_ITEM_SORT_ORDER"SortOrder",
3026 CAFE_DESKTOP_ITEM_SORT_ORDER"SortOrder");
3027 }
3028
3029 return g_hash_table_lookup (strings, key) != NULL((void*)0);
3030}
3031
3032/* If no need to cannonize, returns NULL */
3033static char *
3034cannonize (const char *key, const char *value)
3035{
3036 if (standard_is_boolean (key)) {
3037 if (value[0] == 'T' ||
3038 value[0] == 't' ||
3039 value[0] == 'Y' ||
3040 value[0] == 'y' ||
3041 atoi (value) != 0) {
3042 return g_strdup ("true")g_strdup_inline ("true");
3043 } else {
3044 return g_strdup ("false")g_strdup_inline ("false");
3045 }
3046 } else if (standard_is_strings (key)) {
3047 int len = strlen (value);
3048 if (len == 0 || value[len-1] != ';') {
3049 return g_strconcat (value, ";", NULL((void*)0));
3050 }
3051 }
3052 /* XXX: Perhaps we should canonize numeric values as well, but this
3053 * has caused some subtle problems before so it needs to be done
3054 * carefully if at all */
3055 return NULL((void*)0);
3056}
3057
3058
3059static char *
3060decode_string_and_dup (const char *s)
3061{
3062 char *p = g_malloc (strlen (s) + 1);
3063 char *q = p;
3064
3065 do {
3066 if (*s == '\\'){
3067 switch (*(++s)){
3068 case 's':
3069 *p++ = ' ';
3070 break;
3071 case 't':
3072 *p++ = '\t';
3073 break;
3074 case 'n':
3075 *p++ = '\n';
3076 break;
3077 case '\\':
3078 *p++ = '\\';
3079 break;
3080 case 'r':
3081 *p++ = '\r';
3082 break;
3083 default:
3084 *p++ = '\\';
3085 *p++ = *s;
3086 break;
3087 }
3088 } else {
3089 *p++ = *s;
3090 }
3091 } while (*s++);
3092
3093 return q;
3094}
3095
3096static char *
3097escape_string_and_dup (const char *s)
3098{
3099 char *return_value, *p;
3100 const char *q;
3101 int len = 0;
3102
3103 if (s == NULL((void*)0))
3104 return g_strdup("")g_strdup_inline ("");
3105
3106 q = s;
3107 while (*q){
3108 len++;
3109 if (strchr ("\n\r\t\\", *q) != NULL((void*)0))
3110 len++;
3111 q++;
3112 }
3113 return_value = p = (char *) g_malloc (len + 1);
3114 do {
3115 switch (*s){
3116 case '\t':
3117 *p++ = '\\';
3118 *p++ = 't';
3119 break;
3120 case '\n':
3121 *p++ = '\\';
3122 *p++ = 'n';
3123 break;
3124 case '\r':
3125 *p++ = '\\';
3126 *p++ = 'r';
3127 break;
3128 case '\\':
3129 *p++ = '\\';
3130 *p++ = '\\';
3131 break;
3132 default:
3133 *p++ = *s;
3134 }
3135 } while (*s++);
3136 return return_value;
3137}
3138
3139static gboolean
3140check_locale (const char *locale)
3141{
3142 GIConv cd = g_iconv_open ("UTF-8", locale);
3143 if ((GIConv)-1 == cd)
3144 return FALSE(0);
3145 g_iconv_close (cd);
3146 return TRUE(!(0));
3147}
3148
3149static void
3150insert_locales (GHashTable *encodings, char *enc, ...)
3151{
3152 va_list args;
3153
3154 va_start (args, enc)__builtin_va_start(args, enc);
3155 for (;;) {
3156 char *s;
3157
3158 s = va_arg (args, char *)__builtin_va_arg(args, char *);
3159 if (s == NULL((void*)0))
3160 break;
3161 g_hash_table_insert (encodings, s, enc);
3162 }
3163 va_end (args)__builtin_va_end(args);
3164}
3165
3166/* make a standard conversion table from the desktop standard spec */
3167static GHashTable *
3168init_encodings (void)
3169{
3170 GHashTable *encodings = g_hash_table_new (g_str_hash, g_str_equal);
3171
3172 /* "C" is plain ascii */
3173 insert_locales (encodings, "ASCII", "C", NULL((void*)0));
3174
3175 insert_locales (encodings, "ARMSCII-8", "by", NULL((void*)0));
3176 insert_locales (encodings, "BIG5", "zh_TW", NULL((void*)0));
3177 insert_locales (encodings, "CP1251", "be", "bg", NULL((void*)0));
3178 if (check_locale ("EUC-CN")) {
3179 insert_locales (encodings, "EUC-CN", "zh_CN", NULL((void*)0));
3180 } else {
3181 insert_locales (encodings, "GB2312", "zh_CN", NULL((void*)0));
3182 }
3183 insert_locales (encodings, "EUC-JP", "ja", NULL((void*)0));
3184 insert_locales (encodings, "EUC-KR", "ko", NULL((void*)0));
3185 /*insert_locales (encodings, "GEORGIAN-ACADEMY", NULL);*/
3186 insert_locales (encodings, "GEORGIAN-PS", "ka", NULL((void*)0));
3187 insert_locales (encodings, "ISO-8859-1", "br", "ca", "da", "de", "en", "es", "eu", "fi", "fr", "gl", "it", "nl", "wa", "no", "pt", "pt", "sv", NULL((void*)0));
3188 insert_locales (encodings, "ISO-8859-2", "cs", "hr", "hu", "pl", "ro", "sk", "sl", "sq", "sr", NULL((void*)0));
3189 insert_locales (encodings, "ISO-8859-3", "eo", NULL((void*)0));
3190 insert_locales (encodings, "ISO-8859-5", "mk", "sp", NULL((void*)0));
3191 insert_locales (encodings, "ISO-8859-7", "el", NULL((void*)0));
3192 insert_locales (encodings, "ISO-8859-9", "tr", NULL((void*)0));
3193 insert_locales (encodings, "ISO-8859-13", "lt", "lv", "mi", NULL((void*)0));
3194 insert_locales (encodings, "ISO-8859-14", "ga", "cy", NULL((void*)0));
3195 insert_locales (encodings, "ISO-8859-15", "et", NULL((void*)0));
3196 insert_locales (encodings, "KOI8-R", "ru", NULL((void*)0));
3197 insert_locales (encodings, "KOI8-U", "uk", NULL((void*)0));
3198 if (check_locale ("TCVN-5712")) {
3199 insert_locales (encodings, "TCVN-5712", "vi", NULL((void*)0));
3200 } else {
3201 insert_locales (encodings, "TCVN", "vi", NULL((void*)0));
3202 }
3203 insert_locales (encodings, "TIS-620", "th", NULL((void*)0));
3204 /*insert_locales (encodings, "VISCII", NULL);*/
3205
3206 return encodings;
3207}
3208
3209static const char *
3210get_encoding_from_locale (const char *locale)
3211{
3212 char lang[3];
3213 const char *encoding;
3214 static GHashTable *encodings = NULL((void*)0);
3215
3216 if (locale == NULL((void*)0))
3217 return NULL((void*)0);
3218
3219 /* if locale includes encoding, use it */
3220 encoding = strchr (locale, '.');
3221 if (encoding != NULL((void*)0)) {
3222 return encoding+1;
3223 }
3224
3225 if (encodings == NULL((void*)0))
3226 encodings = init_encodings ();
3227
3228 /* first try the entire locale (at this point ll_CC) */
3229 encoding = g_hash_table_lookup (encodings, locale);
3230 if (encoding != NULL((void*)0))
3231 return encoding;
3232
3233 /* Try just the language */
3234 strncpy (lang, locale, 2);
3235 lang[2] = '\0';
3236 return g_hash_table_lookup (encodings, lang);
3237}
3238
3239static Encoding
3240get_encoding (ReadBuf *rb)
3241{
3242 gboolean old_kde = FALSE(0);
3243 char buf [BUFSIZ8192];
3244 gboolean all_valid_utf8 = TRUE(!(0));
3245
3246 while (readbuf_gets (buf, sizeof (buf), rb) != NULL((void*)0)) {
3247 if (strncmp (CAFE_DESKTOP_ITEM_ENCODING"Encoding",
3248 buf,
3249 strlen (CAFE_DESKTOP_ITEM_ENCODING"Encoding")) == 0) {
3250 char *p = &buf[strlen (CAFE_DESKTOP_ITEM_ENCODING"Encoding")];
3251 if (*p == ' ')
3252 p++;
3253 if (*p != '=')
3254 continue;
3255 p++;
3256 if (*p == ' ')
3257 p++;
3258 if (strcmp (p, "UTF-8") == 0) {
3259 return ENCODING_UTF8;
3260 } else if (strcmp (p, "Legacy-Mixed") == 0) {
3261 return ENCODING_LEGACY_MIXED;
3262 } else {
3263 /* According to the spec we're not supposed
3264 * to read a file like this */
3265 return ENCODING_UNKNOWN;
3266 }
3267 } else if (strcmp ("[KDE Desktop Entry]", buf) == 0) {
3268 old_kde = TRUE(!(0));
3269 /* don't break yet, we still want to support
3270 * Encoding even here */
3271 }
3272 if (all_valid_utf8 && ! g_utf8_validate (buf, -1, NULL((void*)0)))
3273 all_valid_utf8 = FALSE(0);
3274 }
3275
3276 if (old_kde)
3277 return ENCODING_LEGACY_MIXED;
3278
3279 /* try to guess by location */
3280 if (rb->uri != NULL((void*)0) && strstr (rb->uri, "cafe/apps/") != NULL((void*)0)) {
3281 /* old cafe */
3282 return ENCODING_LEGACY_MIXED;
3283 }
3284
3285 /* A dilemma, new KDE files are in UTF-8 but have no Encoding
3286 * info, at this time we really can't tell. The best thing to
3287 * do right now is to just assume UTF-8 if the whole file
3288 * validates as utf8 I suppose */
3289
3290 if (all_valid_utf8)
3291 return ENCODING_UTF8;
3292 else
3293 return ENCODING_LEGACY_MIXED;
3294}
3295
3296static char *
3297decode_string (const char *value, Encoding encoding, const char *locale)
3298{
3299 char *retval = NULL((void*)0);
3300
3301 /* if legacy mixed, then convert */
3302 if (locale != NULL((void*)0) && encoding == ENCODING_LEGACY_MIXED) {
3303 const char *char_encoding = get_encoding_from_locale (locale);
3304 char *utf8_string;
3305 if (char_encoding == NULL((void*)0))
3306 return NULL((void*)0);
3307 if (strcmp (char_encoding, "ASCII") == 0) {
3308 return decode_string_and_dup (value);
3309 }
3310 utf8_string = g_convert (value, -1, "UTF-8", char_encoding,
3311 NULL((void*)0), NULL((void*)0), NULL((void*)0));
3312 if (utf8_string == NULL((void*)0))
3313 return NULL((void*)0);
3314 retval = decode_string_and_dup (utf8_string);
3315 g_free (utf8_string);
3316 return retval;
3317 /* if utf8, then validate */
3318 } else if (locale != NULL((void*)0) && encoding == ENCODING_UTF8) {
3319 if ( ! g_utf8_validate (value, -1, NULL((void*)0)))
3320 /* invalid utf8, ignore this key */
3321 return NULL((void*)0);
3322 return decode_string_and_dup (value);
3323 } else {
3324 /* Meaning this is not a localized string */
3325 return decode_string_and_dup (value);
3326 }
3327}
3328
3329static char *
3330snarf_locale_from_key (const char *key)
3331{
3332 const char *brace;
3333 char *locale, *p;
3334
3335 brace = strchr (key, '[');
3336 if (brace == NULL((void*)0))
3337 return NULL((void*)0);
3338
3339 locale = g_strdup (brace + 1)g_strdup_inline (brace + 1);
3340 if (*locale == '\0') {
3341 g_free (locale);
3342 return NULL((void*)0);
3343 }
3344 p = strchr (locale, ']');
3345 if (p == NULL((void*)0)) {
3346 g_free (locale);
3347 return NULL((void*)0);
3348 }
3349 *p = '\0';
3350 return locale;
3351}
3352
3353static void
3354insert_key (CafeDesktopItem *item,
3355 Section *cur_section,
3356 Encoding encoding,
3357 const char *key,
3358 const char *value,
3359 gboolean old_kde,
3360 gboolean no_translations)
3361{
3362 char *k;
3363 char *val;
3364 /* we always store everything in UTF-8 */
3365 if (cur_section == NULL((void*)0) &&
3366 strcmp (key, CAFE_DESKTOP_ITEM_ENCODING"Encoding") == 0) {
3367 k = g_strdup (key)g_strdup_inline (key);
3368 val = g_strdup ("UTF-8")g_strdup_inline ("UTF-8");
3369 } else {
3370 char *locale = snarf_locale_from_key (key);
3371 /* If we're ignoring translations */
3372 if (no_translations && locale != NULL((void*)0)) {
3373 g_free (locale);
3374 return;
3375 }
3376 val = decode_string (value, encoding, locale);
3377
3378 /* Ignore this key, it's whacked */
3379 if (val == NULL((void*)0)) {
3380 g_free (locale);
3381 return;
3382 }
3383
3384 g_strchomp (val);
3385
3386 /* For old KDE entries, we can also split by a comma
3387 * on sort order, so convert to semicolons */
3388 if (old_kde &&
3389 cur_section == NULL((void*)0) &&
3390 strcmp (key, CAFE_DESKTOP_ITEM_SORT_ORDER"SortOrder") == 0 &&
3391 strchr (val, ';') == NULL((void*)0)) {
3392 int i;
3393 for (i = 0; val[i] != '\0'; i++) {
3394 if (val[i] == ',')
3395 val[i] = ';';
3396 }
3397 }
3398
3399 /* Check some types, not perfect, but catches a lot
3400 * of things */
3401 if (cur_section == NULL((void*)0)) {
3402 char *cannon = cannonize (key, val);
3403 if (cannon != NULL((void*)0)) {
3404 g_free (val);
3405 val = cannon;
3406 }
3407 }
3408
3409 k = g_strdup (key)g_strdup_inline (key);
3410
3411 /* Take care of the language part */
3412 if (locale != NULL((void*)0) &&
3413 strcmp (locale, "C") == 0) {
3414 char *p;
3415 /* Whack C locale */
3416 p = strchr (k, '[');
3417 *p = '\0';
3418 g_free (locale);
3419 } else if (locale != NULL((void*)0)) {
3420 char *p, *brace;
3421
3422 /* Whack the encoding part */
3423 p = strchr (locale, '.');
3424 if (p != NULL((void*)0))
3425 *p = '\0';
3426
3427 if (g_list_find_custom (item->languages, locale,
3428 (GCompareFunc)strcmp) == NULL((void*)0)) {
3429 item->languages = g_list_prepend
3430 (item->languages, locale);
3431 } else {
3432 g_free (locale);
3433 }
3434
3435 /* Whack encoding from encoding in the key */
3436 brace = strchr (k, '[');
3437 p = strchr (brace, '.');
3438 if (p != NULL((void*)0)) {
3439 *p = ']';
3440 *(p+1) = '\0';
3441 }
3442 }
3443 }
3444
3445
3446 if (cur_section == NULL((void*)0)) {
3447 /* only add to list if we haven't seen it before */
3448 if (g_hash_table_lookup (item->main_hash, k) == NULL((void*)0)) {
3449 item->keys = g_list_prepend (item->keys,
3450 g_strdup (k)g_strdup_inline (k));
3451 }
3452 /* later duplicates override earlier ones */
3453 g_hash_table_replace (item->main_hash, k, val);
3454 } else {
3455 char *full = g_strdup_printf
3456 ("%s/%s",
3457 cur_section->name, k);
3458 /* only add to list if we haven't seen it before */
3459 if (g_hash_table_lookup (item->main_hash, full) == NULL((void*)0)) {
3460 cur_section->keys =
3461 g_list_prepend (cur_section->keys, k);
3462 }
3463 /* later duplicates override earlier ones */
3464 g_hash_table_replace (item->main_hash,
3465 full, val);
3466 }
3467}
3468
3469static void
3470setup_type (CafeDesktopItem *item, const char *uri)
3471{
3472 const char *type = g_hash_table_lookup (item->main_hash,
3473 CAFE_DESKTOP_ITEM_TYPE"Type");
3474 if (type == NULL((void*)0) && uri != NULL((void*)0)) {
3475 char *base = g_path_get_basename (uri);
3476 if (base != NULL((void*)0) &&
3477 strcmp (base, ".directory") == 0) {
3478 /* This gotta be a directory */
3479 g_hash_table_replace (item->main_hash,
3480 g_strdup (CAFE_DESKTOP_ITEM_TYPE)g_strdup_inline ("Type"),
3481 g_strdup ("Directory")g_strdup_inline ("Directory"));
3482 item->keys = g_list_prepend
3483 (item->keys, g_strdup (CAFE_DESKTOP_ITEM_TYPE)g_strdup_inline ("Type"));
3484 item->type = CAFE_DESKTOP_ITEM_TYPE_DIRECTORY;
3485 } else {
3486 item->type = CAFE_DESKTOP_ITEM_TYPE_NULL;
3487 }
3488 g_free (base);
3489 } else {
3490 item->type = type_from_string (type);
3491 }
3492}
3493
3494/* fallback to find something suitable for C locale */
3495static char *
3496try_english_key (CafeDesktopItem *item, const char *key)
3497{
3498 char *str;
3499 char *locales[] = { "en_US", "en_GB", "en_AU", "en", NULL((void*)0) };
3500 int i;
3501
3502 str = NULL((void*)0);
3503 for (i = 0; locales[i] != NULL((void*)0) && str == NULL((void*)0); i++) {
3504 str = g_strdup (lookup_locale (item, key, locales[i]))g_strdup_inline (lookup_locale (item, key, locales[i]));
3505 }
3506 if (str != NULL((void*)0)) {
3507 /* We need a 7-bit ascii string, so whack all
3508 * above 127 chars */
3509 guchar *p;
3510 for (p = (guchar *)str; *p != '\0'; p++) {
3511 if (*p > 127)
3512 *p = '?';
3513 }
3514 }
3515 return str;
3516}
3517
3518
3519static void
3520sanitize (CafeDesktopItem *item, const char *uri)
3521{
3522 const char *type;
3523
3524 type = lookup (item, CAFE_DESKTOP_ITEM_TYPE"Type");
3525
3526 /* understand old cafe style url exec thingies */
3527 if (type != NULL((void*)0) && strcmp (type, "URL") == 0) {
3528 const char *exec = lookup (item, CAFE_DESKTOP_ITEM_EXEC"Exec");
3529 set (item, CAFE_DESKTOP_ITEM_TYPE"Type", "Link");
3530 if (exec != NULL((void*)0)) {
3531 /* Note, this must be in this order */
3532 set (item, CAFE_DESKTOP_ITEM_URL"URL", exec);
3533 set (item, CAFE_DESKTOP_ITEM_EXEC"Exec", NULL((void*)0));
3534 }
3535 }
3536
3537 /* we make sure we have Name, Encoding and Version */
3538 if (lookup (item, CAFE_DESKTOP_ITEM_NAME"Name") == NULL((void*)0)) {
3539 char *name = try_english_key (item, CAFE_DESKTOP_ITEM_NAME"Name");
3540 /* If no name, use the basename */
3541 if (name == NULL((void*)0) && uri != NULL((void*)0))
3542 name = g_path_get_basename (uri);
3543 /* If no uri either, use same default as cafe_desktop_item_new */
3544 if (name == NULL((void*)0)) {
3545 /* Translators: the "name" mentioned here is the name of
3546 * an application or a document */
3547 name = g_strdup (_("No name"))g_strdup_inline (((char *) g_dgettext ("cafe-desktop", "No name"
)))
;
3548 }
3549 g_hash_table_replace (item->main_hash,
3550 g_strdup (CAFE_DESKTOP_ITEM_NAME)g_strdup_inline ("Name"),
3551 name);
3552 item->keys = g_list_prepend
3553 (item->keys, g_strdup (CAFE_DESKTOP_ITEM_NAME)g_strdup_inline ("Name"));
3554 }
3555 if (lookup (item, CAFE_DESKTOP_ITEM_ENCODING"Encoding") == NULL((void*)0)) {
3556 /* We store everything in UTF-8 so write that down */
3557 g_hash_table_replace (item->main_hash,
3558 g_strdup (CAFE_DESKTOP_ITEM_ENCODING)g_strdup_inline ("Encoding"),
3559 g_strdup ("UTF-8")g_strdup_inline ("UTF-8"));
3560 item->keys = g_list_prepend
3561 (item->keys, g_strdup (CAFE_DESKTOP_ITEM_ENCODING)g_strdup_inline ("Encoding"));
3562 }
3563 if (lookup (item, CAFE_DESKTOP_ITEM_VERSION"Version") == NULL((void*)0)) {
3564 /* this is the version that we follow, so write it down */
3565 g_hash_table_replace (item->main_hash,
3566 g_strdup (CAFE_DESKTOP_ITEM_VERSION)g_strdup_inline ("Version"),
3567 g_strdup ("1.0")g_strdup_inline ("1.0"));
3568 item->keys = g_list_prepend
3569 (item->keys, g_strdup (CAFE_DESKTOP_ITEM_VERSION)g_strdup_inline ("Version"));
3570 }
3571}
3572
3573enum {
3574 FirstBrace,
3575 OnSecHeader,
3576 IgnoreToEOL,
3577 IgnoreToEOLFirst,
3578 KeyDef,
3579 KeyDefOnKey,
3580 KeyValue
3581};
3582
3583static CafeDesktopItem *
3584ditem_load (ReadBuf *rb,
3585 gboolean no_translations,
3586 GError **error)
3587{
3588 int state;
3589 char CharBuffer [1024];
3590 char *next = CharBuffer;
3591 int c;
3592 Encoding encoding;
3593 CafeDesktopItem *item;
3594 Section *cur_section = NULL((void*)0);
3595 char *key = NULL((void*)0);
3596 gboolean old_kde = FALSE(0);
3597
3598 encoding = get_encoding (rb);
3599 if (encoding == ENCODING_UNKNOWN) {
3600 /* spec says, don't read this file */
3601 g_set_error (error,
3602 CAFE_DESKTOP_ITEM_ERRORcafe_desktop_item_error_quark (),
3603 CAFE_DESKTOP_ITEM_ERROR_UNKNOWN_ENCODING,
3604 _("Unknown encoding of: %s")((char *) g_dgettext ("cafe-desktop", "Unknown encoding of: %s"
))
,
3605 rb->uri);
3606 readbuf_close (rb);
3607 return NULL((void*)0);
3608 }
3609
3610 /* Rewind since get_encoding goes through the file */
3611 if (! readbuf_rewind (rb, error)) {
3612 readbuf_close (rb);
3613 /* spec says, don't read this file */
3614 return NULL((void*)0);
3615 }
3616
3617 item = cafe_desktop_item_new ();
3618 item->modified = FALSE(0);
3619
3620 /* Note: location and mtime are filled in by the new_from_file
3621 * function since it has those values */
3622
3623#define OVERFLOW (next == &CharBuffer [sizeof(CharBuffer)-1])
3624
3625 state = FirstBrace;
3626 while ((c = readbuf_getc (rb)) != EOF(-1)) {
3627 if (c == '\r') /* Ignore Carriage Return */
3628 continue;
3629
3630 switch (state) {
3631
3632 case OnSecHeader:
3633 if (c == ']' || OVERFLOW) {
3634 *next = '\0';
3635 next = CharBuffer;
3636
3637 /* keys were inserted in reverse */
3638 if (cur_section != NULL((void*)0) &&
3639 cur_section->keys != NULL((void*)0)) {
3640 cur_section->keys = g_list_reverse
3641 (cur_section->keys);
3642 }
3643 if (strcmp (CharBuffer,
3644 "KDE Desktop Entry") == 0) {
3645 /* Main section */
3646 cur_section = NULL((void*)0);
3647 old_kde = TRUE(!(0));
3648 } else if (strcmp (CharBuffer,
3649 "Desktop Entry") == 0) {
3650 /* Main section */
3651 cur_section = NULL((void*)0);
3652 } else {
3653 cur_section = g_new0 (Section, 1)((Section *) g_malloc0_n ((1), sizeof (Section)));
3654 cur_section->name =
3655 g_strdup (CharBuffer)g_strdup_inline (CharBuffer);
3656 cur_section->keys = NULL((void*)0);
3657 item->sections = g_list_prepend
3658 (item->sections, cur_section);
3659 }
3660 state = IgnoreToEOL;
3661 } else if (c == '[') {
3662 /* FIXME: probably error out instead of ignoring this */
3663 } else {
3664 *next++ = c;
3665 }
3666 break;
3667
3668 case IgnoreToEOL:
3669 case IgnoreToEOLFirst:
3670 if (c == '\n'){
3671 if (state == IgnoreToEOLFirst)
3672 state = FirstBrace;
3673 else
3674 state = KeyDef;
3675 next = CharBuffer;
3676 }
3677 break;
3678
3679 case FirstBrace:
3680 case KeyDef:
3681 case KeyDefOnKey:
3682 if (c == '#') {
3683 if (state == FirstBrace)
3684 state = IgnoreToEOLFirst;
3685 else
3686 state = IgnoreToEOL;
3687 break;
3688 }
3689
3690 if (c == '[' && state != KeyDefOnKey){
3691 state = OnSecHeader;
3692 next = CharBuffer;
3693 g_free (key);
3694 key = NULL((void*)0);
3695 break;
3696 }
3697 /* On first pass, don't allow dangling keys */
3698 if (state == FirstBrace)
3699 break;
3700
3701 if ((c == ' ' && state != KeyDefOnKey) || c == '\t')
3702 break;
3703
3704 if (c == '\n' || OVERFLOW) { /* Abort Definition */
3705 next = CharBuffer;
3706 state = KeyDef;
3707 break;
3708 }
3709
3710 if (c == '=' || OVERFLOW){
3711 *next = '\0';
3712
3713 g_free (key);
3714 key = g_strdup (CharBuffer)g_strdup_inline (CharBuffer);
3715 state = KeyValue;
3716 next = CharBuffer;
3717 } else {
3718 *next++ = c;
3719 state = KeyDefOnKey;
3720 }
3721 break;
3722
3723 case KeyValue:
3724 if (OVERFLOW || c == '\n'){
3725 *next = '\0';
3726
3727 insert_key (item, cur_section, encoding,
3728 key, CharBuffer, old_kde,
3729 no_translations);
3730
3731 g_free (key);
3732 key = NULL((void*)0);
3733
3734 state = (c == '\n') ? KeyDef : IgnoreToEOL;
3735 next = CharBuffer;
3736 } else {
3737 *next++ = c;
3738 }
3739 break;
3740
3741 } /* switch */
3742
3743 } /* while ((c = getc_unlocked (f)) != EOF) */
3744 if (c == EOF(-1) && state == KeyValue) {
3745 *next = '\0';
3746
3747 insert_key (item, cur_section, encoding,
3748 key, CharBuffer, old_kde,
3749 no_translations);
3750
3751 g_free (key);
3752 key = NULL((void*)0);
3753 }
3754
3755#undef OVERFLOW
3756
3757 /* keys were inserted in reverse */
3758 if (cur_section != NULL((void*)0) &&
3759 cur_section->keys != NULL((void*)0)) {
3760 cur_section->keys = g_list_reverse (cur_section->keys);
3761 }
3762 /* keys were inserted in reverse */
3763 item->keys = g_list_reverse (item->keys);
3764 /* sections were inserted in reverse */
3765 item->sections = g_list_reverse (item->sections);
3766
3767 /* sanitize some things */
3768 sanitize (item, rb->uri);
3769
3770 /* make sure that we set up the type */
3771 setup_type (item, rb->uri);
3772
3773 readbuf_close (rb);
3774
3775 return item;
3776}
3777
3778static void stream_printf (GFileOutputStream *stream,
3779 const char *format, ...) G_GNUC_PRINTF (2, 3)__attribute__((__format__ (__printf__, 2, 3)));
3780
3781static void
3782stream_printf (GFileOutputStream *stream, const char *format, ...)
3783{
3784 va_list args;
3785 gchar *s;
3786
3787 va_start (args, format)__builtin_va_start(args, format);
3788 s = g_strdup_vprintf (format, args);
3789 va_end (args)__builtin_va_end(args);
3790
3791 /* FIXME: what about errors */
3792 g_output_stream_write (G_OUTPUT_STREAM (stream)((((GOutputStream*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((stream)), ((g_output_stream_get_type ()))))))
, s, strlen (s),
3793 NULL((void*)0), NULL((void*)0));
3794 g_free (s);
3795}
3796
3797static void
3798dump_section (CafeDesktopItem *item, GFileOutputStream *stream, Section *section)
3799{
3800 GList *li;
3801
3802 stream_printf (stream, "[%s]\n", section->name);
3803 for (li = section->keys; li != NULL((void*)0); li = li->next) {
3804 const char *key = li->data;
3805 char *full = g_strdup_printf ("%s/%s", section->name, key);
3806 const char *value = g_hash_table_lookup (item->main_hash, full);
3807 if (value != NULL((void*)0)) {
3808 char *val = escape_string_and_dup (value);
3809 stream_printf (stream, "%s=%s\n", key, val);
3810 g_free (val);
3811 }
3812 g_free (full);
3813 }
3814}
3815
3816static gboolean
3817ditem_save (CafeDesktopItem *item, const char *uri, GError **error)
3818{
3819 GList *li;
3820 GFile *file;
3821 GFileOutputStream *stream;
3822
3823 file = g_file_new_for_uri (uri);
3824 stream = g_file_replace (file, NULL((void*)0), FALSE(0), G_FILE_CREATE_NONE,
3825 NULL((void*)0), error);
3826 if (stream == NULL((void*)0))
3827 return FALSE(0);
3828
3829 stream_printf (stream, "[Desktop Entry]\n");
3830 for (li = item->keys; li != NULL((void*)0); li = li->next) {
3831 const char *key = li->data;
3832 const char *value = g_hash_table_lookup (item->main_hash, key);
3833 if (value != NULL((void*)0)) {
3834 char *val = escape_string_and_dup (value);
3835 stream_printf (stream, "%s=%s\n", key, val);
3836 g_free (val);
3837 }
3838 }
3839
3840 if (item->sections != NULL((void*)0))
3841 stream_printf (stream, "\n");
3842
3843 for (li = item->sections; li != NULL((void*)0); li = li->next) {
3844 Section *section = li->data;
3845
3846 /* Don't write empty sections */
3847 if (section->keys == NULL((void*)0))
3848 continue;
3849
3850 dump_section (item, stream, section);
3851
3852 if (li->next != NULL((void*)0))
3853 stream_printf (stream, "\n");
3854 }
3855
3856 g_object_unref (stream);
3857 g_object_unref (file);
3858
3859 return TRUE(!(0));
3860}
3861
3862static gpointer
3863_cafe_desktop_item_copy (gpointer boxed)
3864{
3865 return cafe_desktop_item_copy (boxed);
3866}
3867
3868static void
3869_cafe_desktop_item_free (gpointer boxed)
3870{
3871 cafe_desktop_item_unref (boxed);
3872}
3873
3874GType
3875cafe_desktop_item_get_type (void)
3876{
3877 static GType type = 0;
3878
3879 if (type == 0) {
3880 type = g_boxed_type_register_static ("CafeDesktopItem",
3881 _cafe_desktop_item_copy,
3882 _cafe_desktop_item_free);
3883 }
3884
3885 return type;
3886}
3887
3888GQuark
3889cafe_desktop_item_error_quark (void)
3890{
3891 static GQuark q = 0;
3892 if (q == 0)
3893 q = g_quark_from_static_string ("cafe-desktop-item-error-quark");
3894
3895 return q;
3896}