Compose: add iterator API
[platform/upstream/libxkbcommon.git] / src / compose / paths.c
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 "xkbcommon/xkbcommon.h"
27 #include "utils.h"
28 #include "context.h"
29 #include "paths.h"
30
31 enum resolve_name_direction {
32     LEFT_TO_RIGHT,
33     RIGHT_TO_LEFT,
34 };
35
36 const char *
37 get_xlocaledir_path(struct xkb_context *ctx)
38 {
39     const char *dir = xkb_context_getenv(ctx, "XLOCALEDIR");
40     if (!dir)
41         dir = XLOCALEDIR;
42     return dir;
43 }
44
45 /*
46  * Files like compose.dir have the format LEFT: RIGHT.  Lookup @name in
47  * such a file and return its matching value, according to @direction.
48  * @filename is relative to the xlocaledir.
49  */
50 static char *
51 resolve_name(struct xkb_context *ctx, const char *filename,
52              enum resolve_name_direction direction, const char *name)
53 {
54     int ret;
55     bool ok;
56     const char *xlocaledir;
57     char path[512];
58     FILE *file;
59     char *string;
60     size_t string_size;
61     const char *end;
62     const char *s, *left, *right;
63     char *match;
64     size_t left_len, right_len, name_len;
65
66     xlocaledir = get_xlocaledir_path(ctx);
67
68     ret = snprintf(path, sizeof(path), "%s/%s", xlocaledir, filename);
69     if (ret < 0 || (size_t) ret >= sizeof(path))
70         return false;
71
72     file = fopen(path, "rb");
73     if (!file)
74         return false;
75
76     ok = map_file(file, &string, &string_size);
77     fclose(file);
78     if (!ok)
79         return false;
80
81     s = string;
82     end = string + string_size;
83     name_len = strlen(name);
84     match = NULL;
85
86     while (s < end) {
87         /* Skip spaces. */
88         while (s < end && is_space(*s))
89             s++;
90
91         /* Skip comments. */
92         if (s < end && *s == '#') {
93             while (s < end && *s != '\n')
94                 s++;
95             continue;
96         }
97
98         /* Get the left value. */
99         left = s;
100         while (s < end && !is_space(*s) && *s != ':')
101             s++;
102         left_len = s - left;
103
104         /* There's an optional colon between left and right. */
105         if (s < end && *s == ':')
106             s++;
107
108         /* Skip spaces. */
109         while (s < end && is_space(*s))
110             s++;
111
112         /* Get the right value. */
113         right = s;
114         while (s < end && !is_space(*s))
115             s++;
116         right_len = s - right;
117
118         /* Discard rest of line. */
119         while (s < end && *s != '\n')
120             s++;
121
122         if (direction == LEFT_TO_RIGHT) {
123             if (left_len == name_len && memcmp(left, name, left_len) == 0) {
124                 match = strndup(right, right_len);
125                 break;
126             }
127         }
128         else if (direction == RIGHT_TO_LEFT) {
129             if (right_len == name_len && memcmp(right, name, right_len) == 0) {
130                 match = strndup(left, left_len);
131                 break;
132             }
133         }
134     }
135
136     unmap_file(string, string_size);
137     return match;
138 }
139
140 char *
141 resolve_locale(struct xkb_context *ctx, const char *locale)
142 {
143     char *alias = resolve_name(ctx, "locale.alias", LEFT_TO_RIGHT, locale);
144     return alias ? alias : strdup(locale);
145 }
146
147 char *
148 get_xcomposefile_path(struct xkb_context *ctx)
149 {
150     return strdup_safe(xkb_context_getenv(ctx, "XCOMPOSEFILE"));
151 }
152
153 char *
154 get_xdg_xcompose_file_path(struct xkb_context *ctx)
155 {
156     const char *xdg_config_home;
157     const char *home;
158
159     xdg_config_home = xkb_context_getenv(ctx, "XDG_CONFIG_HOME");
160     if (!xdg_config_home || xdg_config_home[0] != '/') {
161         home = xkb_context_getenv(ctx, "HOME");
162         if (!home)
163             return NULL;
164         return asprintf_safe("%s/.config/XCompose", home);
165     }
166
167     return asprintf_safe("%s/XCompose", xdg_config_home);
168 }
169
170 char *
171 get_home_xcompose_file_path(struct xkb_context *ctx)
172 {
173     const char *home;
174
175     home = xkb_context_getenv(ctx, "HOME");
176     if (!home)
177         return NULL;
178
179     return asprintf_safe("%s/.XCompose", home);
180 }
181
182 char *
183 get_locale_compose_file_path(struct xkb_context *ctx, const char *locale)
184 {
185     char *resolved;
186     char *path;
187
188     /*
189      * WARNING: Random workaround ahead.
190      *
191      * We currently do not support non-UTF-8 Compose files.  The C/POSIX
192      * locale is specified to be the default fallback locale with an
193      * ASCII charset.  But for some reason the compose.dir points the C
194      * locale to the iso8859-1/Compose file, which is not ASCII but
195      * ISO8859-1.  Since this is bound to happen a lot, and since our API
196      * is UTF-8 based, and since 99% of the time a C locale is really just
197      * a misconfiguration for UTF-8, let's do the most helpful thing.
198      */
199     if (streq(locale, "C"))
200         locale = "en_US.UTF-8";
201
202     resolved = resolve_name(ctx, "compose.dir", RIGHT_TO_LEFT, locale);
203     if (!resolved)
204         return NULL;
205
206     if (resolved[0] == '/') {
207         path = resolved;
208     }
209     else {
210         const char *xlocaledir = get_xlocaledir_path(ctx);
211         path = asprintf_safe("%s/%s", xlocaledir, resolved);
212         free(resolved);
213     }
214
215     return path;
216 }