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