Bug Summary

File:libcafe-desktop/cafe-desktop-item.c
Warning:line 3117, column 4
Access of the heap area at index 4, while it holds only 4 'char' elements

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/2025-04-08-090954-15183-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) &&
1
Assuming 'under' is not equal to NULL
824 ! force &&
825 ! item->modified)
826 return TRUE(!(0));
827
828 if (under
1.1
'under' is not equal to NULL
== NULL((void*)0))
2
Taking false branch
829 uri = item->location;
830 else
831 uri = under;
832
833 if (uri
2.1
'uri' is not equal to NULL
== NULL((void*)0)) {
3
Taking false branch
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))
4
Calling 'ditem_save'
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;
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)
;
1776
1777 if (item->type == 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 == NULL((void*)0) && !use_current_dir)
1785 working_dir = g_get_home_dir ();
1786
1787 if (cafe_desktop_item_get_boolean (item, CAFE_DESKTOP_ITEM_TERMINAL"Terminal")) {
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)
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 ||
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) {
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)) {
1877 exec_locale = g_strdup ("")g_strdup_inline ("");
1878 }
1879
1880 do {
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 == 0 && added_status == ADDED_NONE && append_uris) {
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 == 0 && added_status == ADDED_NONE && append_paths) {
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 > 0 && added_status == ADDED_NONE) {
1910 g_free (new_exec);
1911 break;
1912 }
1913
1914 if ( ! g_shell_parse_argv (new_exec,
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++)
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++)
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 != 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,
1975 real_argv,
1976 envp,
1977 (do_not_reap_child ? G_SPAWN_DO_NOT_REAP_CHILD : 0) | G_SPAWN_SEARCH_PATH /* flags */,
1978 NULL((void*)0), /* child_setup_func */
1979 NULL((void*)0), /* child_setup_func_data */
1980 (do_not_reap_child ? &pid : NULL((void*)0)) /* child_pid */,
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) {
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))
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 == 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 != NULL((void*)0)) {
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)
2022 g_strfreev (term_argv);
2023
2024 if (free_me)
2025 g_strfreev (free_me);
2026
2027 return ret;
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
15.1
's' is not equal to NULL
== NULL((void*)0))
16
Taking false branch
3104 return g_strdup("")g_strdup_inline ("");
3105
3106 q = s;
3107 while (*q){
17
Loop condition is true. Entering loop body
20
Loop condition is true. Entering loop body
23
Loop condition is true. Entering loop body
26
Loop condition is false. Execution continues on line 3113
3108 len++;
3109 if (strchr ("\n\r\t\\", *q) != NULL((void*)0))
18
Assuming the condition is false
19
Taking false branch
21
Assuming the condition is false
22
Taking false branch
24
Assuming the condition is false
25
Taking false branch
3110 len++;
3111 q++;
3112 }
3113 return_value = p = (char *) g_malloc (len + 1);
3114 do {
29
Loop condition is true. Execution continues on line 3115
32
Loop condition is true. Execution continues on line 3115
3115 switch (*s){
27
Control jumps to 'case 92:' at line 3128
30
Control jumps to 'case 92:' at line 3128
33
Control jumps to 'case 9:' at line 3116
3116 case '\t':
3117 *p++ = '\\';
34
Access of the heap area at index 4, while it holds only 4 'char' elements
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;
28
Execution continues on line 3135
31
Execution continues on line 3135
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))
5
Assuming 'stream' is not equal to NULL
6
Taking false branch
3827 return FALSE(0);
3828
3829 stream_printf (stream, "[Desktop Entry]\n");
3830 for (li = item->keys; li != NULL((void*)0); li = li->next) {
7
Assuming 'li' is not equal to NULL
8
Loop condition is true. Entering loop body
11
Assuming 'li' is not equal to NULL
12
Loop condition is true. Entering loop body
3831 const char *key = li->data;
3832 const char *value = g_hash_table_lookup (item->main_hash, key);
3833 if (value != NULL((void*)0)) {
9
Assuming 'value' is not equal to NULL
10
Taking true branch
13
Assuming 'value' is not equal to NULL
14
Taking true branch
3834 char *val = escape_string_and_dup (value);
15
Calling 'escape_string_and_dup'
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}