/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
* ezfc-font-config.c
* Copyright (C) 2011-2018 Akira TAGOH
*
* Authors:
* Akira TAGOH <akira@tagoh.org>
*
* This library is free software: you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation, either
* version 3 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <glib/gstdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include "ezfc-font-config-private.h"
/**
* SECTION: ezfc-font-config
* @Short_Description: A class to hold font configuration information
* @Title: ezfc_font_config_t
*
* This class provides an access to the font configuration information
* from fontconfig.
*
* Since: 0.14
*/
/*< private >*/
/*< protected >*/
gboolean
ezfc_font_config_set_name(ezfc_font_config_t *info,
const gchar *name)
{
ezfc_font_config_private_t *priv = (ezfc_font_config_private_t *)info;
g_return_val_if_fail (info != NULL, FALSE);
g_return_val_if_fail (name != NULL, FALSE);
if (priv->name)
ezfc_mem_remove_ref(&priv->parent, priv->name);
priv->name = g_strdup(name);
ezfc_mem_add_ref(&priv->parent, priv->name, g_free);
return TRUE;
}
gboolean
ezfc_font_config_set_description(ezfc_font_config_t *info,
const gchar *desc)
{
ezfc_font_config_private_t *priv = (ezfc_font_config_private_t *)info;
g_return_val_if_fail (info != NULL, FALSE);
g_return_val_if_fail (desc != NULL, FALSE);
if (priv->desc)
ezfc_mem_remove_ref(&priv->parent, priv->desc);
priv->desc = g_strdup(desc);
ezfc_mem_add_ref(&priv->parent, priv->desc, g_free);
return TRUE;
}
gboolean
ezfc_font_config_set_enabled(ezfc_font_config_t *info,
gboolean is_enabled)
{
ezfc_font_config_private_t *priv = (ezfc_font_config_private_t *)info;
g_return_val_if_fail (info != NULL, FALSE);
priv->is_enabled = is_enabled;
return TRUE;
}
void
ezfc_font_config_set_config_type(ezfc_font_config_t *info,
gint type)
{
ezfc_font_config_private_t *priv = (ezfc_font_config_private_t *)info;
g_return_if_fail (info != NULL);
priv->type = type;
}
/*< public >*/
/**
* ezfc_font_config_new:
*
* Create an instance of #ezfc_font_config_t.
*
* Returns: a #ezfc_font_config_t.
*/
ezfc_font_config_t *
ezfc_font_config_new(void)
{
ezfc_font_config_private_t *retval = ezfc_mem_alloc_object(sizeof (ezfc_font_config_private_t));
return (ezfc_font_config_t *)retval;
}
/**
* ezfc_font_config_ref:
* @info: a #ezfc_font_config_t.
*
* Increases the reference count of @info.
*
* Returns: (transfer none): the same @info object.
*/
ezfc_font_config_t *
ezfc_font_config_ref(ezfc_font_config_t *info)
{
ezfc_font_config_private_t *priv = (ezfc_font_config_private_t *)info;
return ezfc_mem_ref(&priv->parent);
}
/**
* ezfc_font_config_unref:
* @info: a #ezfc_font_config_t.
*
* Decreases the reference count of @info. when its reference count
* drops to 0, the object is finalized (i.e. its memory is freed).
*/
void
ezfc_font_config_unref(ezfc_font_config_t *info)
{
ezfc_font_config_private_t *priv = (ezfc_font_config_private_t *)info;
if (priv)
ezfc_mem_unref(&priv->parent);
}
/**
* ezfc_font_config_get_name:
* @info: a #ezfc_font_config_t.
*
* Obtain the configuration name being stored in @info object.
*
* Returns: the configuration filename
*/
const gchar *
ezfc_font_config_get_name(ezfc_font_config_t *info)
{
ezfc_font_config_private_t *priv = (ezfc_font_config_private_t *)info;
g_return_val_if_fail (info != NULL, NULL);
return priv->name;
}
/**
* ezfc_font_config_get_description:
* @info: a #ezfc_font_config_t.
*
* Obtain the description for the configuration
*
* Returns: the description string
*/
const gchar *
ezfc_font_config_get_description(ezfc_font_config_t *info)
{
ezfc_font_config_private_t *priv = (ezfc_font_config_private_t *)info;
g_return_val_if_fail (info != NULL, NULL);
return priv->desc;
}
/**
* ezfc_font_config_is_enabled:
* @info: a #ezfc_font_config_t
*
* Returns %TRUE if the configuration is enabled in fontconfig, otherwise %FALSE.
*
* Returns: a boolean value
*/
gboolean
ezfc_font_config_is_enabled(ezfc_font_config_t *info)
{
ezfc_font_config_private_t *priv = (ezfc_font_config_private_t *)info;
g_return_val_if_fail (info != NULL, FALSE);
return priv->is_enabled;
}
/**
* ezfc_font_config_set_enable:
* @info: a #ezfc_font_config_t
* @flag: a boolean value
* @error: (allow-none): a #GError to store an error if any, or %NULL.
*
* Enable/Disable a configuration.
*
* Returns: %TRUE if successfully enabled or disabled, otherwise %FALSE.
*/
gboolean
ezfc_font_config_set_enable(ezfc_font_config_t *info,
gboolean flag,
GError **error)
{
ezfc_font_config_private_t *priv = (ezfc_font_config_private_t *)info;
gboolean retval = FALSE;
g_return_val_if_fail (info != NULL, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
if (priv->is_enabled != flag) {
gint save_errno = 0;
if (flag) {
GList *ll, *l = ezfc_font_config_get_config_dirs();
for (ll = l; ll; ll = g_list_next(ll)) {
const gchar *d = ll->data;
if (g_access(d, W_OK) == 0) {
gchar *fn = g_path_get_basename(priv->name);
gchar *lfn = g_build_filename(d, fn, NULL);
if (symlink(priv->name, lfn) == -1) {
if (error) {
save_errno = errno;
g_set_error(error, G_FILE_ERROR,
g_file_error_from_errno(save_errno),
"%s", g_strerror(save_errno));
}
} else {
g_free(priv->name);
priv->name = g_strdup(lfn);
priv->is_enabled = TRUE;
retval = TRUE;
}
g_free(fn);
g_free(lfn);
break;
}
}
g_list_free(l);
} else {
if (g_file_test(priv->name, G_FILE_TEST_IS_SYMLINK)) {
gchar *lfn = g_file_read_link(priv->name, error);
if (lfn) {
if (g_unlink(priv->name) == -1) {
if (error) {
save_errno = errno;
g_set_error(error, G_FILE_ERROR,
g_file_error_from_errno(save_errno),
"%s", g_strerror(save_errno));
}
} else {
g_free(priv->name);
priv->name = g_strdup(lfn);
priv->is_enabled = FALSE;
retval = TRUE;
}
g_free(lfn);
}
}
}
}
return retval;
}
/**
* ezfc_font_config_is_user_conf:
* @info: a #ezfc_font_config_t
*
* Returns %TRUE if the configuration is an user config otherwise %FALSE.
*
* Returns: a boolean value
*/
gboolean
ezfc_font_config_is_user_conf(ezfc_font_config_t *info)
{
ezfc_font_config_private_t *priv = (ezfc_font_config_private_t *)info;
g_return_val_if_fail (info != NULL, FALSE);
return (priv->type & EZFC_FONT_CONFIG_TYPE_USER) != 0;
}
/**
* ezfc_font_config_is_system_conf:
* @info: a #ezfc_font_config_t
*
* Returns %TRUE if the configuration is a system config otherwise %FALSE.
*
* Returns: a boolean value
*/
gboolean
ezfc_font_config_is_system_conf(ezfc_font_config_t *info)
{
ezfc_font_config_private_t *priv = (ezfc_font_config_private_t *)info;
g_return_val_if_fail (info != NULL, FALSE);
return (priv->type & EZFC_FONT_CONFIG_TYPE_SYSTEM) != 0;
}
/**
* ezfc_font_config_is_writable:
* @info:a #ezfc_font_config_t
*
* Returns %TRUE if the configuration is writable to be disabled/enabled otherwise %FALSE
*
* Returns: a boolean value
*/
gboolean
ezfc_font_config_is_writable(ezfc_font_config_t *info)
{
ezfc_font_config_private_t *priv = (ezfc_font_config_private_t *)info;
g_return_val_if_fail (info != NULL, FALSE);
return access(priv->name, W_OK) == 0 && g_file_test(priv->name, G_FILE_TEST_IS_SYMLINK);
}
/**
* ezfc_font_config_get_list:
*
* Obtains the list of #ezfc_font_config_t to see what kind of configuration
* files for fontconfig are installed and enabled on the system.
*
* Returns: (element-type EzfcFontConfig) (transfer full): a #GList contains #ezfc_font_config_t.
*/
GList *
ezfc_font_config_get_list(void)
{
FcConfig *config = NULL;
if (!config)
config = FcConfigGetCurrent();
return ezfc_font_config_get_list_from(config);
}
/**
* ezfc_font_config_get_list_from:
* @config: a #FcConfig.
*
* Obtains the list of #ezfc_font_config_t to see what kind of configuration
* files for fontconfig are installed and enabled on the system from @config.
*
* Returns: (element-type EzfcFontConfig) (transfer full): a #GList contains #ezfc_font_config_t.
*/
GList *
ezfc_font_config_get_list_from(FcConfig *config)
{
FcConfigFileInfoIter iter;
GList *retval = NULL;
FcChar8 *name = NULL, *desc = NULL;
FcBool enabled;
ezfc_font_config_t *p;
const gchar *uconfigdir = g_get_user_config_dir();
size_t len = strlen(uconfigdir);
g_return_val_if_fail (config != NULL, NULL);
FcConfigFileInfoIterInit(config, &iter);
do {
gint type = 0;
if (!FcConfigFileInfoIterGet(config, &iter,
&name, &desc, &enabled))
break;
p = ezfc_font_config_new();
if (!p)
goto bail;
ezfc_font_config_set_name(p, (const gchar *)name);
ezfc_font_config_set_description(p, (const gchar *)desc);
ezfc_font_config_set_enabled(p, enabled);
if (strncmp((const char *)name, uconfigdir, len) == 0) {
if (!g_file_test((const gchar *)name, G_FILE_TEST_IS_SYMLINK))
type |= EZFC_FONT_CONFIG_TYPE_USER;
} else {
FcStrList *l = FcConfigGetConfigDirs(NULL);
FcChar8 *s;
const gchar *e = g_getenv("FONTCONFIG_FILE");
while ((s = FcStrListNext(l))) {
size_t len = strlen((const char *)s);
if (strncmp((const char *)name, (const char *)s, len) == 0) {
type |= EZFC_FONT_CONFIG_TYPE_SYSTEM;
break;
}
}
FcStrListDone(l);
if (e && strcmp((const char *)name, e) == 0)
type |= EZFC_FONT_CONFIG_TYPE_SYSTEM;
else if (strcmp((const char *)name, "fonts.conf") == 0)
type |= EZFC_FONT_CONFIG_TYPE_SYSTEM;
else {
gchar *fn = g_path_get_basename((const gchar *)name);
if (strcmp(fn, "fonts.conf") == 0)
type |= EZFC_FONT_CONFIG_TYPE_SYSTEM;
g_free(fn);
}
}
ezfc_font_config_set_config_type(p, type);
if (name) {
FcStrFree(name);
name = NULL;
}
if (desc) {
FcStrFree(desc);
desc = NULL;
}
retval = g_list_append(retval, p);
} while (FcConfigFileInfoIterNext (config, &iter));
return retval;
bail:
if (name) {
FcStrFree(name);
name = NULL;
}
if (desc) {
FcStrFree(desc);
desc = NULL;
}
if (retval)
g_list_free_full(retval, (GDestroyNotify)ezfc_font_config_unref);
return NULL;
}
/**
* ezfc_font_config_get_config_dirs:
*
* Obtains the list of the config directories where fontconfig can recognizes
* and enables the configuration files.
*
* Returns: (element-type utf8) (transfer none): a #GList contains a string
*/
GList *
ezfc_font_config_get_config_dirs(void)
{
FcConfig *config = NULL;
if (!config)
config = FcConfigGetCurrent();
return ezfc_font_config_get_config_dirs_from(config);
}
/**
* ezfc_font_config_get_config_dirs_from:
* @config: a #FcConfig.
*
* Obtains the list of the config directories where fontconfig can recognizes
* and enables the configuration files from @config.
*
* Returns: (element-type utf8) (transfer none): a #GList contains a string
*/
GList *
ezfc_font_config_get_config_dirs_from(FcConfig *config)
{
FcStrList *l = FcConfigGetConfigDirs(config);
FcChar8 *s;
GList *ret = NULL;
while ((s = FcStrListNext (l))) {
ret = g_list_append(ret, (gpointer)s);
}
FcStrListDone(l);
return ret;
}