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