1 /*
2  * Copyright © 2014 Ran Benita <ran234@gmail.com>
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice (including the next
12  * paragraph) shall be included in all copies or substantial portions of the
13  * Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21  * DEALINGS IN THE SOFTWARE.
22  */
23 
24 #include "config.h"
25 
26 #include "utils.h"
27 #include "paths.h"
28 #include "utils.h"
29 
30 enum resolve_name_direction {
31     LEFT_TO_RIGHT,
32     RIGHT_TO_LEFT,
33 };
34 
35 const char *
get_xlocaledir_path(void)36 get_xlocaledir_path(void)
37 {
38     const char *dir = secure_getenv("XLOCALEDIR");
39     if (!dir)
40         dir = XLOCALEDIR;
41     return dir;
42 }
43 
44 /*
45  * Files like compose.dir have the format LEFT: RIGHT.  Lookup @name in
46  * such a file and return its matching value, according to @direction.
47  * @filename is relative to the xlocaledir.
48  */
49 static char *
resolve_name(const char * filename,enum resolve_name_direction direction,const char * name)50 resolve_name(const char *filename, enum resolve_name_direction direction,
51              const char *name)
52 {
53     int ret;
54     bool ok;
55     const char *xlocaledir;
56     char path[512];
57     FILE *file;
58     char *string;
59     size_t string_size;
60     const char *end;
61     const char *s, *left, *right;
62     char *match;
63     size_t left_len, right_len, name_len;
64 
65     xlocaledir = get_xlocaledir_path();
66 
67     ret = snprintf(path, sizeof(path), "%s/%s", xlocaledir, filename);
68     if (ret < 0 || (size_t) ret >= sizeof(path))
69         return false;
70 
71     file = fopen(path, "rb");
72     if (!file)
73         return false;
74 
75     ok = map_file(file, &string, &string_size);
76     fclose(file);
77     if (!ok)
78         return false;
79 
80     s = string;
81     end = string + string_size;
82     name_len = strlen(name);
83     match = NULL;
84 
85     while (s < end) {
86         /* Skip spaces. */
87         while (s < end && is_space(*s))
88             s++;
89 
90         /* Skip comments. */
91         if (s < end && *s == '#') {
92             while (s < end && *s != '\n')
93                 s++;
94             continue;
95         }
96 
97         /* Get the left value. */
98         left = s;
99         while (s < end && !is_space(*s) && *s != ':')
100             s++;
101         left_len = s - left;
102 
103         /* There's an optional colon between left and right. */
104         if (s < end && *s == ':')
105             s++;
106 
107         /* Skip spaces. */
108         while (s < end && is_space(*s))
109             s++;
110 
111         /* Get the right value. */
112         right = s;
113         while (s < end && !is_space(*s))
114             s++;
115         right_len = s - right;
116 
117         /* Discard rest of line. */
118         while (s < end && *s != '\n')
119             s++;
120 
121         if (direction == LEFT_TO_RIGHT) {
122             if (left_len == name_len && memcmp(left, name, left_len) == 0) {
123                 match = strndup(right, right_len);
124                 break;
125             }
126         }
127         else if (direction == RIGHT_TO_LEFT) {
128             if (right_len == name_len && memcmp(right, name, right_len) == 0) {
129                 match = strndup(left, left_len);
130                 break;
131             }
132         }
133     }
134 
135     unmap_file(string, string_size);
136     return match;
137 }
138 
139 char *
resolve_locale(const char * locale)140 resolve_locale(const char *locale)
141 {
142     char *alias = resolve_name("locale.alias", LEFT_TO_RIGHT, locale);
143     return alias ? alias : strdup(locale);
144 }
145 
146 char *
get_xcomposefile_path(void)147 get_xcomposefile_path(void)
148 {
149     return strdup_safe(secure_getenv("XCOMPOSEFILE"));
150 }
151 
152 char *
get_xdg_xcompose_file_path(void)153 get_xdg_xcompose_file_path(void)
154 {
155     const char *xdg_config_home;
156     const char *home;
157 
158     xdg_config_home = secure_getenv("XDG_CONFIG_HOME");
159     if (!xdg_config_home || xdg_config_home[0] != '/') {
160         home = secure_getenv("HOME");
161         if (!home)
162             return NULL;
163         return asprintf_safe("%s/.config/XCompose", home);
164     }
165 
166     return asprintf_safe("%s/XCompose", xdg_config_home);
167 }
168 
169 char *
get_home_xcompose_file_path(void)170 get_home_xcompose_file_path(void)
171 {
172     const char *home;
173 
174     home = secure_getenv("HOME");
175     if (!home)
176         return NULL;
177 
178     return asprintf_safe("%s/.XCompose", home);
179 }
180 
181 char *
get_locale_compose_file_path(const char * locale)182 get_locale_compose_file_path(const char *locale)
183 {
184     char *resolved;
185     char *path;
186 
187     /*
188      * WARNING: Random workaround ahead.
189      *
190      * We currently do not support non-UTF-8 Compose files.  The C/POSIX
191      * locale is specified to be the default fallback locale with an
192      * ASCII charset.  But for some reason the compose.dir points the C
193      * locale to the iso8859-1/Compose file, which is not ASCII but
194      * ISO8859-1.  Since this is bound to happen a lot, and since our API
195      * is UTF-8 based, and since 99% of the time a C locale is really just
196      * a misconfiguration for UTF-8, let's do the most helpful thing.
197      */
198     if (streq(locale, "C"))
199         locale = "en_US.UTF-8";
200 
201     resolved = resolve_name("compose.dir", RIGHT_TO_LEFT, locale);
202     if (!resolved)
203         return NULL;
204 
205     if (resolved[0] == '/') {
206         path = resolved;
207     }
208     else {
209         const char *xlocaledir = get_xlocaledir_path();
210         path = asprintf_safe("%s/%s", xlocaledir, resolved);
211         free(resolved);
212     }
213 
214     return path;
215 }
216