EFL 1.7 svn doobies
[profile/ivi/efreet.git] / src / lib / efreet_base.c
1 #ifdef HAVE_CONFIG_H
2 # include <config.h>
3 #endif
4
5 #undef alloca
6 #ifdef HAVE_ALLOCA_H
7 # include <alloca.h>
8 #elif defined __GNUC__
9 # define alloca __builtin_alloca
10 #elif defined _AIX
11 # define alloca __alloca
12 #elif defined _MSC_VER
13 # include <malloc.h>
14 # define alloca _alloca
15 #else
16 # include <stddef.h>
17 # ifdef  __cplusplus
18 extern "C"
19 # endif
20 void *alloca (size_t);
21 #endif
22
23 #include <unistd.h>
24
25 #ifdef _WIN32
26 # include <winsock2.h>
27 #endif
28
29 /* define macros and variable for using the eina logging system  */
30 #define EFREET_MODULE_LOG_DOM _efreet_base_log_dom
31 static int _efreet_base_log_dom = -1;
32
33 #include "Efreet.h"
34 #include "efreet_private.h"
35
36 static Efreet_Version _version = { VMAJ, VMIN, VMIC, VREV };
37 EAPI Efreet_Version *efreet_version = &_version;
38
39 #ifdef _WIN32
40 # define EFREET_PATH_SEP ';'
41 #else
42 # define EFREET_PATH_SEP ':'
43 #endif
44
45 static const char *efreet_home_dir = NULL;
46 static const char *xdg_data_home = NULL;
47 static const char *xdg_config_home = NULL;
48 static const char *xdg_cache_home = NULL;
49 static Eina_List  *xdg_data_dirs = NULL;
50 static Eina_List  *xdg_config_dirs = NULL;
51 static const char *xdg_desktop_dir = NULL;
52 static const char *hostname = NULL;
53
54 static const char *efreet_dir_get(const char *key, const char *fallback);
55 static Eina_List  *efreet_dirs_get(const char *key,
56                                         const char *fallback);
57 static const char *efreet_user_dir_get(const char *key, const char *fallback);
58
59 /**
60  * @internal
61  * @return Returns @c 1 on success or @c 0 on failure
62  * @brief Initializes the efreet base settings
63  */
64 int
65 efreet_base_init(void)
66 {
67     _efreet_base_log_dom = eina_log_domain_register
68       ("efreet_base", EFREET_DEFAULT_LOG_COLOR);
69     if (_efreet_base_log_dom < 0)
70     {
71         EINA_LOG_ERR("Efreet: Could not create a log domain for efreet_base.\n");
72         return 0;
73     }
74     return 1;
75 }
76
77 /**
78  * @internal
79  * @return Returns no value
80  * @brief Cleans up the efreet base settings system
81  */
82 void
83 efreet_base_shutdown(void)
84 {
85     IF_RELEASE(efreet_home_dir);
86     IF_RELEASE(xdg_desktop_dir);
87     IF_RELEASE(xdg_data_home);
88     IF_RELEASE(xdg_config_home);
89     IF_RELEASE(xdg_cache_home);
90
91     IF_FREE_LIST(xdg_data_dirs, eina_stringshare_del);
92     IF_FREE_LIST(xdg_config_dirs, eina_stringshare_del);
93
94     IF_RELEASE(hostname);
95
96     eina_log_domain_unregister(_efreet_base_log_dom);
97     _efreet_base_log_dom = -1;
98 }
99
100 /**
101  * @internal
102  * @return Returns the users home directory
103  * @brief Gets the users home directory and returns it.
104  */
105 const char *
106 efreet_home_dir_get(void)
107 {
108     if (efreet_home_dir) return efreet_home_dir;
109
110     efreet_home_dir = getenv("HOME");
111 #ifdef _WIN32
112     if (!efreet_home_dir || efreet_home_dir[0] == '\0')
113         efreet_home_dir = getenv("USERPROFILE");
114 #endif
115     if (!efreet_home_dir || efreet_home_dir[0] == '\0')
116         efreet_home_dir = "/tmp";
117
118     efreet_home_dir = eina_stringshare_add(efreet_home_dir);
119
120     return efreet_home_dir;
121 }
122
123 EAPI const char *
124 efreet_desktop_dir_get(void)
125 {
126     if (xdg_desktop_dir) return xdg_desktop_dir;
127     xdg_desktop_dir = efreet_user_dir_get("XDG_DESKTOP_DIR", _("Desktop"));
128     return xdg_desktop_dir;
129 }
130
131 EAPI const char *
132 efreet_data_home_get(void)
133 {
134     if (xdg_data_home) return xdg_data_home;
135     xdg_data_home = efreet_dir_get("XDG_DATA_HOME", "/.local/share");
136     return xdg_data_home;
137 }
138
139 EAPI Eina_List *
140 efreet_data_dirs_get(void)
141 {
142 #ifdef _WIN32
143     char buf[4096];
144 #endif
145
146     if (xdg_data_dirs) return xdg_data_dirs;
147
148 #ifdef _WIN32
149     snprintf(buf, 4096, "%s\\Efl;" PACKAGE_DATA_DIR ";/usr/share;/usr/local/share", getenv("APPDATA"));
150     xdg_data_dirs = efreet_dirs_get("XDG_DATA_DIRS", buf);
151 #else
152     xdg_data_dirs = efreet_dirs_get("XDG_DATA_DIRS",
153                             PACKAGE_DATA_DIR ":/usr/share:/usr/local/share");
154 #endif
155     return xdg_data_dirs;
156 }
157
158 EAPI const char *
159 efreet_config_home_get(void)
160 {
161     if (xdg_config_home) return xdg_config_home;
162     xdg_config_home = efreet_dir_get("XDG_CONFIG_HOME", "/.config");
163     return xdg_config_home;
164 }
165
166 EAPI Eina_List *
167 efreet_config_dirs_get(void)
168 {
169     if (xdg_config_dirs) return xdg_config_dirs;
170     xdg_config_dirs = efreet_dirs_get("XDG_CONFIG_DIRS", "/etc/xdg");
171     return xdg_config_dirs;
172 }
173
174 EAPI const char *
175 efreet_cache_home_get(void)
176 {
177     if (xdg_cache_home) return xdg_cache_home;
178     xdg_cache_home = efreet_dir_get("XDG_CACHE_HOME", "/.cache");
179     return xdg_cache_home;
180 }
181
182 EAPI const char *
183 efreet_hostname_get(void)
184 {
185     char buf[256];
186
187     if (hostname) return hostname;
188     if (gethostname(buf, sizeof(buf)) < 0)
189         hostname = eina_stringshare_add("");
190     else
191         hostname = eina_stringshare_add(buf);
192     return hostname;
193 }
194
195 void
196 efreet_dirs_reset(void)
197 {
198     eina_stringshare_replace(&xdg_desktop_dir, NULL);
199 }
200
201 /**
202  * @internal
203  * @param key The environment key to lookup
204  * @param fallback The fallback value to use
205  * @return Returns the directory related to the given key or the fallback
206  * @brief This tries to determine the correct directory name given the
207  * environment key @a key and fallbacks @a fallback.
208  */
209 static const char *
210 efreet_dir_get(const char *key, const char *fallback)
211 {
212     char *dir;
213     const char *t;
214
215     dir = getenv(key);
216     if (!dir || dir[0] == '\0')
217     {
218         int len;
219         const char *user;
220
221         user = efreet_home_dir_get();
222         len = strlen(user) + strlen(fallback) + 1;
223         dir = alloca(len);
224         snprintf(dir, len, "%s%s", user, fallback);
225
226         t = eina_stringshare_add(dir);
227     }
228     else t = eina_stringshare_add(dir);
229
230     return t;
231 }
232
233 /**
234  * @internal
235  * @param key The environment key to lookup
236  * @param fallback The fallback value to use
237  * @return Returns a list of directories specified by the given key @a key
238  * or from the list of fallbacks in @a fallback.
239  * @brief Creates a list of directories as given in the environment key @a
240  * key or from the fallbacks in @a fallback
241  */
242 static Eina_List *
243 efreet_dirs_get(const char *key, const char *fallback)
244 {
245     Eina_List *dirs = NULL;
246     const char *path;
247     char *tmp, *s, *p;
248     char ts[PATH_MAX];
249     size_t len;
250
251     path = getenv(key);
252     if (!path || (path[0] == '\0')) path = fallback;
253
254     if (!path) return dirs;
255
256     len = strlen(path) + 1;
257     tmp = alloca(len);
258     memcpy(tmp, path, len);
259     s = tmp;
260     p = strchr(s, EFREET_PATH_SEP);
261     while (p)
262     {
263         *p = '\0';
264         if (!eina_list_search_unsorted(dirs, EINA_COMPARE_CB(strcmp), s))
265         {
266             // resolve path properly/fully to remove path//path2 to
267             // path/path2, path/./path2 to path/path2 etc.
268             if (realpath(s, ts))
269                 dirs = eina_list_append(dirs, (void *)eina_stringshare_add(ts));
270         }
271
272         s = ++p;
273         p = strchr(s, EFREET_PATH_SEP);
274     }
275     if (!eina_list_search_unsorted(dirs, EINA_COMPARE_CB(strcmp), s))
276     {
277         // resolve path properly/fully to remove path//path2 to
278         // path/path2, path/./path2 to path/path2 etc.
279         if (realpath(s, ts))
280             dirs = eina_list_append(dirs, (void *)eina_stringshare_add(ts));
281     }
282
283     return dirs;
284 }
285
286 static const char *
287 efreet_env_expand(const char *in)
288 {
289    Eina_Strbuf *sb;
290    const char *ret, *p, *e1 = NULL, *e2 = NULL, *val;
291    char *env;
292
293    if (!in) return NULL;
294    sb = eina_strbuf_new();
295    if (!sb) return NULL;
296    
297    /* maximum length of any env var is the input string */
298    env = alloca(strlen(in) + 1);
299    for (p = in; *p; p++)
300      {
301         if (!e1)
302           {
303              if (*p == '$') e1 = p + 1;
304              else eina_strbuf_append_char(sb, *p);
305           }
306         else if (!(((*p >= 'a') && (*p <= 'z')) ||
307                    ((*p >= 'A') && (*p <= 'Z')) ||
308                    ((*p >= '0') && (*p <= '9')) ||
309                    (*p == '_')))
310           {
311              size_t len;
312              
313              e2 = p;
314              len = (size_t)(e2 - e1);
315              if (len > 0)
316                {
317                   memcpy(env, e1, len);
318                   env[len] = 0;
319                   val = getenv(env);
320                   if (val) eina_strbuf_append(sb, val);
321                }
322              e1 = NULL;
323              eina_strbuf_append_char(sb, *p);
324           }
325      }
326    ret = eina_stringshare_add(eina_strbuf_string_get(sb));
327    eina_strbuf_free(sb);
328    return ret;
329 }
330
331 /**
332  * @internal
333  * @param key The user-dirs key to lookup
334  * @param fallback The fallback value to use
335  * @return Returns the directory related to the given key or the fallback
336  * @brief This tries to determine the correct directory name given the
337  * user-dirs key @a key and fallbacks @a fallback.
338  */
339 static const char *
340 efreet_user_dir_get(const char *key, const char *fallback)
341 {
342    Eina_File *file = NULL;
343    Eina_File_Line *line;
344    Eina_Iterator *it = NULL;
345    const char *config_home;
346    char path[PATH_MAX];
347    char *ret = NULL;
348
349    config_home = efreet_config_home_get();
350    snprintf(path, sizeof(path), "%s/user-dirs.dirs", config_home);
351
352    file = eina_file_open(path, EINA_FALSE);
353    if (!file) goto fallback;
354    it = eina_file_map_lines(file);
355    if (!it) goto fallback;
356    EINA_ITERATOR_FOREACH(it, line)
357      {
358         const char *eq, *end;
359
360         if (line->length < 3) continue;
361         if (line->start[0] == '#') continue;
362         if (strncmp(line->start, "XDG", 3)) continue;
363         eq = memchr(line->start, '=', line->length);
364         if (!eq) continue;
365         if (strncmp(key, line->start, eq - line->start)) continue;
366         if (++eq >= line->end) continue;
367         if (*eq != '"') continue;
368         if (++eq >= line->end) continue;
369         end = memchr(eq, '"', line->end - eq);
370         if (!end) continue;
371         ret = alloca(end - eq + 1);
372         memcpy(ret, eq, end - eq);
373         ret[end - eq] = '\0';
374         break;
375      }
376 fallback:
377    if (it) eina_iterator_free(it);
378    if (file) eina_file_close(file);
379    if (!ret)
380      {
381         const char *home;
382         home = efreet_home_dir_get();
383         ret = alloca(strlen(home) + strlen(fallback) + 2);
384         sprintf(ret, "%s/%s", home, fallback);
385      }
386    return efreet_env_expand(ret);
387 }