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