C++程序  |  205行  |  5.33 KB

/*
 * Copyright © 2014 Ran Benita <ran234@gmail.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice (including the next
 * paragraph) shall be included in all copies or substantial portions of the
 * Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */

#include "utils.h"
#include "paths.h"

enum resolve_name_direction {
    LEFT_TO_RIGHT,
    RIGHT_TO_LEFT,
};

const char *
get_xlocaledir_path(void)
{
    const char *dir = secure_getenv("XLOCALEDIR");
    if (!dir)
        dir = XLOCALEDIR;
    return dir;
}

/*
 * Files like compose.dir have the format LEFT: RIGHT.  Lookup @name in
 * such a file and return its matching value, according to @direction.
 * @filename is relative to the xlocaledir.
 */
static char *
resolve_name(const char *filename, enum resolve_name_direction direction,
             const char *name)
{
    int ret;
    bool ok;
    const char *xlocaledir;
    char path[512];
    FILE *file;
    const char *string, *end;
    size_t string_size;
    const char *s, *left, *right;
    char *match;
    size_t left_len, right_len, name_len;

    xlocaledir = get_xlocaledir_path();

    ret = snprintf(path, sizeof(path), "%s/%s", xlocaledir, filename);
    if (ret < 0 || (size_t) ret >= sizeof(path))
        return false;

    file = fopen(path, "r");
    if (!file)
        return false;

    ok = map_file(file, &string, &string_size);
    fclose(file);
    if (!ok)
        return false;

    s = string;
    end = string + string_size;
    name_len = strlen(name);
    match = NULL;

    while (s < end) {
        /* Skip spaces. */
        while (s < end && is_space(*s))
            s++;

        /* Skip comments. */
        if (s < end && *s == '#') {
            while (s < end && *s != '\n')
                s++;
            continue;
        }

        /* Get the left value. */
        left = s;
        while (s < end && !is_space(*s) && *s != ':')
            s++;
        left_len = s - left;

        /* There's an optional colon between left and right. */
        if (s < end && *s == ':')
            s++;

        /* Skip spaces. */
        while (s < end && is_space(*s))
            s++;

        /* Get the right value. */
        right = s;
        while (s < end && !is_space(*s))
            s++;
        right_len = s - right;

        /* Discard rest of line. */
        while (s < end && *s != '\n')
            s++;

        if (direction == LEFT_TO_RIGHT) {
            if (left_len == name_len && memcmp(left, name, left_len) == 0) {
                match = strndup(right, right_len);
                break;
            }
        }
        else if (direction == RIGHT_TO_LEFT) {
            if (right_len == name_len && memcmp(right, name, right_len) == 0) {
                match = strndup(left, left_len);
                break;
            }
        }
    }

    unmap_file(string, string_size);
    return match;
}

char *
resolve_locale(const char *locale)
{
    char *alias = resolve_name("locale.alias", LEFT_TO_RIGHT, locale);
    return alias ? alias : strdup(locale);
}

const char *
get_xcomposefile_path(void)
{
    return secure_getenv("XCOMPOSEFILE");
}

char *
get_home_xcompose_file_path(void)
{
    int ret;
    const char *home;
    char *path;

    home = secure_getenv("HOME");
    if (!home)
        return NULL;

    ret = asprintf(&path, "%s/.XCompose", home);
    if (ret <0)
        return NULL;

    return path;
}

char *
get_locale_compose_file_path(const char *locale)
{
    int ret;
    const char *xlocaledir;
    char *resolved;
    char *path;

    /*
     * WARNING: Random workaround ahead.
     *
     * We currently do not support non-UTF-8 Compose files.  The C/POSIX
     * locale is specified to be the default fallback locale with an
     * ASCII charset.  But for some reason the compose.dir points the C
     * locale to the iso8859-1/Compose file, which is not ASCII but
     * ISO8859-1.  Since this is bound to happen a lot, and since our API
     * is UTF-8 based, and since 99% of the time a C locale is really just
     * a misconfiguration for UTF-8, let's do the most helpful thing.
     */
    if (streq(locale, "C"))
        locale = "en_US.UTF-8";

    resolved = resolve_name("compose.dir", RIGHT_TO_LEFT, locale);
    if (!resolved)
        return NULL;

    if (resolved[0] == '/') {
        path = resolved;
    }
    else {
        xlocaledir = get_xlocaledir_path();
        ret = asprintf(&path, "%s/%s", xlocaledir, resolved);
        free(resolved);
        if (ret < 0)
            return NULL;
    }

    return path;
}