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