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