1 /* -*- Mode: C ; c-basic-offset: 2 -*- */
3 * LADI Session Handler (ladish)
5 * Copyright (C) 2008, 2009 Nedko Arnaudov <nedko@arnaudov.name>
7 **************************************************************************
8 * This file contains code of the application database
9 **************************************************************************
11 * LADI Session Handler is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * LADI Session Handler is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with LADI Session Handler. If not, see <http://www.gnu.org/licenses/>
23 * or write to the Free Software Foundation, Inc.,
24 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
30 #include <sys/types.h>
35 #include "../common/debug.h"
39 lash_appdb_free_entry(
40 struct lash_appdb_entry
* entry_ptr
);
42 #define MAP_TYPE_STRING 0
43 #define MAP_TYPE_BOOL 1
52 struct map g_appdb_entry_map
[] =
56 .type
= MAP_TYPE_STRING
,
57 .offset
= offsetof(struct lash_appdb_entry
, name
)
61 .type
= MAP_TYPE_STRING
,
62 .offset
= offsetof(struct lash_appdb_entry
, generic_name
)
66 .type
= MAP_TYPE_STRING
,
67 .offset
= offsetof(struct lash_appdb_entry
, comment
)
71 .type
= MAP_TYPE_STRING
,
72 .offset
= offsetof(struct lash_appdb_entry
, icon
)
76 .type
= MAP_TYPE_STRING
,
77 .offset
= offsetof(struct lash_appdb_entry
, exec
)
81 .type
= MAP_TYPE_STRING
,
82 .offset
= offsetof(struct lash_appdb_entry
, path
)
86 .type
= MAP_TYPE_BOOL
,
87 .offset
= offsetof(struct lash_appdb_entry
, terminal
)
100 #define MAX_ENTRIES 1000
105 const char * var_name
,
106 const char * default_value
)
110 value
= getenv(var_name
);
112 /* Spec says that if variable is "either not set or empty", default should be used */
113 if (value
== NULL
|| strlen(value
) == 0)
115 return default_value
;
130 len
= strlen(string
);
131 len_suffix
= strlen(suffix
);
133 if (len
<= len_suffix
)
138 if (memcmp(string
+ (len
- len_suffix
), ".desktop", len_suffix
) != 0)
149 const char * file_path
,
150 char ** data_ptr_ptr
)
158 *data_ptr_ptr
= NULL
;
160 file
= fopen(file_path
, "r");
163 lash_error("Failed to open '%s' for reading", file_path
);
167 if (fseek(file
, 0, SEEK_END
) == -1)
169 lash_error("fseek('%s') failed", file_path
);
176 lash_error("ftell('%s') failed", file_path
);
180 data_ptr
= malloc(size
+ 1);
181 if (data_ptr
== NULL
)
183 lash_error("Failed to allocate %ld bytes for data of file '%s'", size
+ 1, file_path
);
188 if (fseek(file
, 0, SEEK_SET
) == -1)
190 lash_error("fseek('%s') failed", file_path
);
194 if (fread(data_ptr
, size
, 1, file
) != 1)
196 lash_error("Failed to read %ld bytes of data from file '%s'", size
, file_path
);
202 *data_ptr_ptr
= data_ptr
;
216 strlstrip(char * string
)
218 while (*string
== ' ' || *string
== '\t')
227 strrstrip(char * string
)
231 temp
= string
+ strlen(string
);
233 while (temp
> string
)
237 if (*temp
== ' ' || *temp
== '\t')
245 lash_appdb_parse_file_data(
247 struct entry
* entries_array
,
263 next_line
= strchr(line
, '\n');
264 if (next_line
!= NULL
)
266 //lash_info("there is next line");
271 //lash_info("Line '%s'", line);
273 /* skip comments (and empty lines) */
274 if (*line
== 0 || *line
== '#')
281 /* first real line should be begining of "Desktop Entry" group */
282 if (strcmp(line
, "[Desktop Entry]") != 0)
291 value
= strchr(line
, '=');
302 value
= strlstrip(value
);
304 //lash_info("Key=%s", line);
305 //lash_info("Value=%s", value);
307 if (count
+ 1 == max_count
)
309 lash_error("failed to parse desktop entry with more than %u keys", (unsigned int)max_count
);
313 entries_array
->key
= line
;
314 entries_array
->value
= value
;
318 while ((line
= next_line
) != NULL
);
328 struct entry
* entries
,
334 for (i
= 0 ; i
< count
; i
++)
336 if (strcmp(entries
[i
].key
, key
) == 0)
338 return entries
[i
].value
;
346 lash_appdb_load_file(
347 struct list_head
* appdb
,
348 const char * file_path
)
352 struct entry entries
[MAX_ENTRIES
];
353 size_t entries_count
;
357 struct list_head
* node_ptr
;
358 struct lash_appdb_entry
* entry_ptr
;
359 struct map
* map_ptr
;
363 //lash_info("Desktop entry '%s'", file_path);
367 if (!load_file_data(file_path
, &data
))
378 if (!lash_appdb_parse_file_data(data
, entries
, MAX_ENTRIES
, &entries_count
))
383 //lash_info("%llu entries", (unsigned long long)entries_count);
385 /* check whether entry is of "Application" type */
386 value
= lash_appdb_find_key(entries
, entries_count
, "Type");
387 if (value
== NULL
|| strcmp(value
, "Application") != 0)
392 /* check whether "Name" is preset, it is required */
393 name
= lash_appdb_find_key(entries
, entries_count
, "Name");
399 /* check whether entry has LIBLASH or LASHCLASS key */
400 xlash
= lash_appdb_find_key(entries
, entries_count
, "X-LASH");
406 /* check whether entry already exists (first found entries have priority according to XDG Base Directory Specification) */
407 list_for_each(node_ptr
, appdb
)
409 entry_ptr
= list_entry(node_ptr
, struct lash_appdb_entry
, siblings
);
411 if (strcmp(entry_ptr
->name
, name
) == 0)
417 //lash_info("Application '%s' found", name);
419 /* allocate new entry */
420 entry_ptr
= malloc(sizeof(struct lash_appdb_entry
));
421 if (entry_ptr
== NULL
)
423 lash_error("malloc() failed");
427 memset(entry_ptr
, 0, sizeof(struct lash_appdb_entry
));
430 map_ptr
= g_appdb_entry_map
;
431 while (map_ptr
->key
!= NULL
)
433 value
= lash_appdb_find_key(entries
, entries_count
, map_ptr
->key
);
436 assert(strcmp(map_ptr
->key
, "Name") != 0); /* name is required and we already checked this */
441 //lash_info("mapping key '%s' to '%s'", map_ptr->key, value);
443 if (map_ptr
->type
== MAP_TYPE_STRING
)
445 str_ptr_ptr
= (char **)((char *)entry_ptr
+ map_ptr
->offset
);
446 *str_ptr_ptr
= strdup(value
);
447 if (*str_ptr_ptr
== NULL
)
449 lash_error("strdup() failed");
450 goto fail_free_entry
;
453 else if (map_ptr
->type
== MAP_TYPE_BOOL
)
455 bool_ptr
= (bool *)((char *)entry_ptr
+ map_ptr
->offset
);
456 if (strcmp(value
, "true") == 0)
460 else if (strcmp(value
, "false") == 0)
466 lash_error("Ignoring %s:%s bool with wrong value '%s'", name
, map_ptr
->key
, value
);
472 goto fail_free_entry
;
478 /* add entry to appdb list */
479 list_add_tail(&entry_ptr
->siblings
, appdb
);
484 lash_appdb_free_entry(entry_ptr
);
498 struct list_head
* appdb
,
499 const char * base_directory
)
501 char * directory_path
;
504 struct dirent
* dentry_ptr
;
507 //lash_info("lash_appdb_load_dir() called for '%s'.", base_directory);
511 directory_path
= catdup(base_directory
, "/applications/");
512 if (directory_path
== NULL
)
514 lash_error("catdup() failed to compose the appdb dir path");
518 //lash_info("Scanning directory '%s'", directory_path);
520 dir
= opendir(directory_path
);
523 while ((dentry_ptr
= readdir(dir
)) != NULL
)
525 if (dentry_ptr
->d_type
!= DT_REG
)
530 if (!suffix_match(dentry_ptr
->d_name
, ".desktop"))
535 file_path
= catdup(directory_path
, dentry_ptr
->d_name
);
536 if (file_path
== NULL
)
538 lash_error("catdup() failed to compose the appdb dir file");
542 if (!lash_appdb_load_file(appdb
, file_path
))
556 //lash_info("failed to open directory '%s'", directory_path);
562 free(directory_path
);
569 lash_appdb_load_dirs(
570 struct list_head
* appdb
,
571 const char * base_directories
)
577 directories
= strdup(base_directories
);
578 if (directories
== NULL
)
580 lash_error("strdup() failed");
584 directory
= directories
;
588 limiter
= strchr(directory
, ':');
594 if (!lash_appdb_load_dir(appdb
, directory
))
600 directory
= limiter
+ 1;
602 while (limiter
!= NULL
);
611 struct list_head
* appdb
)
613 const char * data_home
;
614 char * data_home_default
;
615 const char * data_dirs
;
616 const char * home_dir
;
621 INIT_LIST_HEAD(appdb
);
623 //lash_info("lash_appdb_load() called.");
625 home_dir
= getenv("HOME");
626 if (home_dir
== NULL
)
628 lash_error("HOME environment variable is not set.");
632 data_home_default
= catdup(home_dir
, "/.local/share");
633 if (data_home_default
== NULL
)
635 lash_error("catdup failed to compose data_home_default");
639 data_home
= get_xdg_var("XDG_DATA_HOME", data_home_default
);
641 if (!lash_appdb_load_dir(appdb
, data_home
))
643 goto fail_free_data_home_default
;
646 data_dirs
= get_xdg_var("XDG_DATA_DIRS", "/usr/local/share/:/usr/share/");
648 if (!lash_appdb_load_dirs(appdb
, data_dirs
))
650 goto fail_free_data_home_default
;
655 fail_free_data_home_default
:
656 free(data_home_default
);
661 lash_appdb_free(appdb
);
668 lash_appdb_free_entry(
669 struct lash_appdb_entry
* entry_ptr
)
671 //lash_info("lash_appdb_free_entry() called.");
673 if (entry_ptr
->name
!= NULL
)
675 free(entry_ptr
->name
);
678 if (entry_ptr
->generic_name
!= NULL
)
680 free(entry_ptr
->generic_name
);
683 if (entry_ptr
->comment
!= NULL
)
685 free(entry_ptr
->comment
);
688 if (entry_ptr
->icon
!= NULL
)
690 free(entry_ptr
->icon
);
693 if (entry_ptr
->exec
!= NULL
)
695 free(entry_ptr
->exec
);
698 if (entry_ptr
->path
!= NULL
)
700 free(entry_ptr
->path
);
708 struct list_head
* appdb
)
710 struct list_head
* node_ptr
;
711 struct lash_appdb_entry
* entry_ptr
;
713 //lash_info("lash_appdb_free() called.");
715 while (!list_empty(appdb
))
717 node_ptr
= appdb
->next
;
718 entry_ptr
= list_entry(node_ptr
, struct lash_appdb_entry
, siblings
);
722 //lash_info("Destroying appdb entry '%s'", entry_ptr->name);
724 lash_appdb_free_entry(entry_ptr
);