12c393a42Smrg/*
2a6844aabSmrg * fontconfig/fc-cat/fc-cat.c
32c393a42Smrg *
42c393a42Smrg * Copyright © 2002 Keith Packard
52c393a42Smrg *
62c393a42Smrg * Permission to use, copy, modify, distribute, and sell this software and its
72c393a42Smrg * documentation for any purpose is hereby granted without fee, provided that
82c393a42Smrg * the above copyright notice appear in all copies and that both that
92c393a42Smrg * copyright notice and this permission notice appear in supporting
10ca08ab68Smrg * documentation, and that the name of the author(s) not be used in
112c393a42Smrg * advertising or publicity pertaining to distribution of the software without
12ca08ab68Smrg * specific, written prior permission.  The authors make no
132c393a42Smrg * representations about the suitability of this software for any purpose.  It
142c393a42Smrg * is provided "as is" without express or implied warranty.
152c393a42Smrg *
16a6844aabSmrg * THE AUTHOR(S) DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
172c393a42Smrg * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
18a6844aabSmrg * EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY SPECIAL, INDIRECT OR
192c393a42Smrg * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
202c393a42Smrg * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
212c393a42Smrg * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
222c393a42Smrg * PERFORMANCE OF THIS SOFTWARE.
232c393a42Smrg */
242c393a42Smrg
252c393a42Smrg#ifdef HAVE_CONFIG_H
262c393a42Smrg#include <config.h>
272c393a42Smrg#else
282c393a42Smrg#ifdef linux
292c393a42Smrg#define HAVE_GETOPT_LONG 1
302c393a42Smrg#endif
312c393a42Smrg#define HAVE_GETOPT 1
322c393a42Smrg#endif
332c393a42Smrg
342c393a42Smrg#include <fontconfig/fontconfig.h>
35ca08ab68Smrg#include "../src/fcarch.h"
362c393a42Smrg#include <stdio.h>
372c393a42Smrg#include <stdlib.h>
382c393a42Smrg#include <string.h>
39a4e54154Smrg#ifdef HAVE_UNISTD_H
402c393a42Smrg#include <unistd.h>
41a4e54154Smrg#endif
422c393a42Smrg#include <sys/types.h>
432c393a42Smrg#include <sys/stat.h>
442c393a42Smrg#include <errno.h>
45a32e9e42Smrg#include <locale.h>
46a32e9e42Smrg
47a32e9e42Smrg#ifdef ENABLE_NLS
48a32e9e42Smrg#include <libintl.h>
49a32e9e42Smrg#define _(x)		(dgettext(GETTEXT_PACKAGE, x))
50a32e9e42Smrg#else
51a32e9e42Smrg#define dgettext(d, s)	(s)
52a32e9e42Smrg#define _(x)		(x)
53a32e9e42Smrg#endif
542c393a42Smrg
552c393a42Smrg#ifndef HAVE_GETOPT
562c393a42Smrg#define HAVE_GETOPT 0
572c393a42Smrg#endif
582c393a42Smrg#ifndef HAVE_GETOPT_LONG
592c393a42Smrg#define HAVE_GETOPT_LONG 0
602c393a42Smrg#endif
612c393a42Smrg
622c393a42Smrg#if HAVE_GETOPT_LONG
632c393a42Smrg#undef  _GNU_SOURCE
642c393a42Smrg#define _GNU_SOURCE
652c393a42Smrg#include <getopt.h>
662c393a42Smrgconst struct option longopts[] = {
672c393a42Smrg    {"version", 0, 0, 'V'},
682c393a42Smrg    {"verbose", 0, 0, 'v'},
692c393a42Smrg    {"recurse", 0, 0, 'r'},
70a6844aabSmrg    {"help", 0, 0, 'h'},
712c393a42Smrg    {NULL,0,0,0},
722c393a42Smrg};
732c393a42Smrg#else
742c393a42Smrg#if HAVE_GETOPT
752c393a42Smrgextern char *optarg;
762c393a42Smrgextern int optind, opterr, optopt;
772c393a42Smrg#endif
782c393a42Smrg#endif
792c393a42Smrg
802c393a42Smrg/*
81c9710b42Smrg * POSIX has broken stdio so that putc must do thread-safe locking,
822c393a42Smrg * this is a serious performance problem for applications doing large
83c9710b42Smrg * amounts of IO with putc (as is done here).  If available, use
84a4e54154Smrg * the putc_unlocked variant instead.
852c393a42Smrg */
862c393a42Smrg
87c9710b42Smrg#if defined(putc_unlocked) || defined(_IO_putc_unlocked)
882c393a42Smrg#define PUTC(c,f) putc_unlocked(c,f)
892c393a42Smrg#else
902c393a42Smrg#define PUTC(c,f) putc(c,f)
912c393a42Smrg#endif
922c393a42Smrg
932c393a42Smrgstatic FcBool
942c393a42Smrgwrite_chars (FILE *f, const FcChar8 *chars)
952c393a42Smrg{
962c393a42Smrg    FcChar8    c;
972c393a42Smrg    while ((c = *chars++))
982c393a42Smrg    {
992c393a42Smrg	switch (c) {
1002c393a42Smrg	case '"':
1012c393a42Smrg	case '\\':
1022c393a42Smrg	    if (PUTC ('\\', f) == EOF)
1032c393a42Smrg		return FcFalse;
1042c393a42Smrg	    /* fall through */
1052c393a42Smrg	default:
1062c393a42Smrg	    if (PUTC (c, f) == EOF)
1072c393a42Smrg		return FcFalse;
1082c393a42Smrg	}
1092c393a42Smrg    }
1102c393a42Smrg    return FcTrue;
1112c393a42Smrg}
1122c393a42Smrg
1132c393a42Smrgstatic FcBool
1142c393a42Smrgwrite_ulong (FILE *f, unsigned long t)
1152c393a42Smrg{
1162c393a42Smrg    int	    pow;
1172c393a42Smrg    unsigned long   temp, digit;
1182c393a42Smrg
1192c393a42Smrg    temp = t;
1202c393a42Smrg    pow = 1;
1212c393a42Smrg    while (temp >= 10)
1222c393a42Smrg    {
1232c393a42Smrg	temp /= 10;
1242c393a42Smrg	pow *= 10;
1252c393a42Smrg    }
1262c393a42Smrg    temp = t;
1272c393a42Smrg    while (pow)
1282c393a42Smrg    {
1292c393a42Smrg	digit = temp / pow;
1302c393a42Smrg	if (PUTC ((char) digit + '0', f) == EOF)
1312c393a42Smrg	    return FcFalse;
1322c393a42Smrg	temp = temp - pow * digit;
1332c393a42Smrg	pow = pow / 10;
1342c393a42Smrg    }
1352c393a42Smrg    return FcTrue;
1362c393a42Smrg}
1372c393a42Smrg
1382c393a42Smrgstatic FcBool
1392c393a42Smrgwrite_int (FILE *f, int i)
1402c393a42Smrg{
1412c393a42Smrg    return write_ulong (f, (unsigned long) i);
1422c393a42Smrg}
1432c393a42Smrg
1442c393a42Smrgstatic FcBool
1452c393a42Smrgwrite_string (FILE *f, const FcChar8 *string)
1462c393a42Smrg{
1472c393a42Smrg
1482c393a42Smrg    if (PUTC ('"', f) == EOF)
1492c393a42Smrg	return FcFalse;
1502c393a42Smrg    if (!write_chars (f, string))
1512c393a42Smrg	return FcFalse;
1522c393a42Smrg    if (PUTC ('"', f) == EOF)
1532c393a42Smrg	return FcFalse;
1542c393a42Smrg    return FcTrue;
1552c393a42Smrg}
1562c393a42Smrg
1572c393a42Smrgstatic void
158a6844aabSmrgusage (char *program, int error)
1592c393a42Smrg{
160a6844aabSmrg    FILE *file = error ? stderr : stdout;
1612c393a42Smrg#if HAVE_GETOPT_LONG
162a32e9e42Smrg    fprintf (file, _("usage: %s [-rv] [--recurse] [--verbose] [*-%s" FC_CACHE_SUFFIX "|directory]...\n"),
1632c393a42Smrg	     program, FC_ARCHITECTURE);
164a6844aabSmrg    fprintf (file, "       %s [-Vh] [--version] [--help]\n", program);
1652c393a42Smrg#else
166a32e9e42Smrg    fprintf (file, _("usage: %s [-rvVh] [*-%s" FC_CACHE_SUFFIX "|directory]...\n"),
1672c393a42Smrg	     program, FC_ARCHITECTURE);
1682c393a42Smrg#endif
169a32e9e42Smrg    fprintf (file, _("Reads font information cache from:\n"));
170a32e9e42Smrg    fprintf (file, _(" 1) specified fontconfig cache file\n"));
171a32e9e42Smrg    fprintf (file, _(" 2) related to a particular font directory\n"));
172a6844aabSmrg    fprintf (file, "\n");
1732c393a42Smrg#if HAVE_GETOPT_LONG
174a32e9e42Smrg    fprintf (file, _("  -r, --recurse        recurse into subdirectories\n"));
175a32e9e42Smrg    fprintf (file, _("  -v, --verbose        be verbose\n"));
176a32e9e42Smrg    fprintf (file, _("  -V, --version        display font config version and exit\n"));
177a32e9e42Smrg    fprintf (file, _("  -h, --help           display this help and exit\n"));
1782c393a42Smrg#else
179a32e9e42Smrg    fprintf (file, _("  -r         (recurse) recurse into subdirectories\n"));
180a32e9e42Smrg    fprintf (file, _("  -v         (verbose) be verbose\n"));
181a32e9e42Smrg    fprintf (file, _("  -V         (version) display font config version and exit\n"));
182a32e9e42Smrg    fprintf (file, _("  -h         (help)    display this help and exit\n"));
1832c393a42Smrg#endif
184a6844aabSmrg    exit (error);
1852c393a42Smrg}
1862c393a42Smrg
1872c393a42Smrg/*
1882c393a42Smrg * return the path from the directory containing 'cache' to 'file'
1892c393a42Smrg */
1902c393a42Smrg
1912c393a42Smrgstatic const FcChar8 *
1922c393a42Smrgfile_base_name (const FcChar8 *cache, const FcChar8 *file)
1932c393a42Smrg{
1942c393a42Smrg    int		    cache_len = strlen ((char *) cache);
1952c393a42Smrg
1962c393a42Smrg    if (!strncmp ((char *) cache, (char *) file, cache_len) && file[cache_len] == '/')
1972c393a42Smrg	return file + cache_len + 1;
1982c393a42Smrg    return file;
1992c393a42Smrg}
2002c393a42Smrg
2012c393a42Smrg#define FC_FONT_FILE_DIR	((FcChar8 *) ".dir")
2022c393a42Smrg
2032c393a42Smrgstatic FcBool
2042c393a42Smrgcache_print_set (FcFontSet *set, FcStrSet *dirs, const FcChar8 *base_name, FcBool verbose)
2052c393a42Smrg{
206ca08ab68Smrg    FcChar8	    *dir;
207ca08ab68Smrg    const FcChar8   *base;
2082c393a42Smrg    int		    n;
2092c393a42Smrg    int		    ndir = 0;
2102c393a42Smrg    FcStrList	    *list;
2112c393a42Smrg
2122c393a42Smrg    list = FcStrListCreate (dirs);
2132c393a42Smrg    if (!list)
2142c393a42Smrg	goto bail2;
2152c393a42Smrg
2162c393a42Smrg    while ((dir = FcStrListNext (list)))
2172c393a42Smrg    {
2182c393a42Smrg	base = file_base_name (base_name, dir);
2192c393a42Smrg	if (!write_string (stdout, base))
2202c393a42Smrg	    goto bail3;
2212c393a42Smrg	if (PUTC (' ', stdout) == EOF)
2222c393a42Smrg	    goto bail3;
2232c393a42Smrg	if (!write_int (stdout, 0))
2242c393a42Smrg	    goto bail3;
2252c393a42Smrg        if (PUTC (' ', stdout) == EOF)
2262c393a42Smrg	    goto bail3;
2272c393a42Smrg	if (!write_string (stdout, FC_FONT_FILE_DIR))
2282c393a42Smrg	    goto bail3;
2292c393a42Smrg	if (PUTC ('\n', stdout) == EOF)
2302c393a42Smrg	    goto bail3;
2312c393a42Smrg	ndir++;
2322c393a42Smrg    }
2332c393a42Smrg
2342c393a42Smrg    for (n = 0; n < set->nfont; n++)
2352c393a42Smrg    {
2362c393a42Smrg	FcPattern   *font = set->fonts[n];
237ca08ab68Smrg	FcChar8 *s;
2382c393a42Smrg
239ca08ab68Smrg	s = FcPatternFormat (font, (const FcChar8 *) "%{=fccat}\n");
240ca08ab68Smrg	if (s)
241ca08ab68Smrg	{
242ca08ab68Smrg	    printf ("%s", s);
243ca08ab68Smrg	    FcStrFree (s);
244ca08ab68Smrg	}
2452c393a42Smrg    }
2462c393a42Smrg    if (verbose && !set->nfont && !ndir)
2472c393a42Smrg	printf ("<empty>\n");
248ca08ab68Smrg
2492c393a42Smrg    FcStrListDone (list);
2502c393a42Smrg
2512c393a42Smrg    return FcTrue;
252ca08ab68Smrg
2532c393a42Smrgbail3:
2542c393a42Smrg    FcStrListDone (list);
2552c393a42Smrgbail2:
2562c393a42Smrg    return FcFalse;
2572c393a42Smrg}
2582c393a42Smrg
2592c393a42Smrgint
2602c393a42Smrgmain (int argc, char **argv)
2612c393a42Smrg{
2622c393a42Smrg    int		i;
2632c393a42Smrg    int		ret = 0;
2642c393a42Smrg    FcFontSet	*fs;
2652c393a42Smrg    FcStrSet    *dirs;
2662c393a42Smrg    FcStrSet	*args = NULL;
2672c393a42Smrg    FcStrList	*arglist;
2682c393a42Smrg    FcCache	*cache;
2692c393a42Smrg    FcConfig	*config;
2702c393a42Smrg    FcChar8	*arg;
2712c393a42Smrg    int		verbose = 0;
2722c393a42Smrg    int		recurse = 0;
2732c393a42Smrg    FcBool	first = FcTrue;
2742c393a42Smrg#if HAVE_GETOPT_LONG || HAVE_GETOPT
2752c393a42Smrg    int		c;
2762c393a42Smrg
277a32e9e42Smrg    setlocale (LC_ALL, "");
2782c393a42Smrg#if HAVE_GETOPT_LONG
279a6844aabSmrg    while ((c = getopt_long (argc, argv, "Vvrh", longopts, NULL)) != -1)
2802c393a42Smrg#else
281a6844aabSmrg    while ((c = getopt (argc, argv, "Vvrh")) != -1)
2822c393a42Smrg#endif
2832c393a42Smrg    {
2842c393a42Smrg	switch (c) {
2852c393a42Smrg	case 'V':
2862c393a42Smrg	    fprintf (stderr, "fontconfig version %d.%d.%d\n",
2872c393a42Smrg		     FC_MAJOR, FC_MINOR, FC_REVISION);
2882c393a42Smrg	    exit (0);
2892c393a42Smrg	case 'v':
2902c393a42Smrg	    verbose++;
2912c393a42Smrg	    break;
2922c393a42Smrg	case 'r':
2932c393a42Smrg	    recurse++;
2942c393a42Smrg	    break;
295a6844aabSmrg	case 'h':
296a6844aabSmrg	    usage (argv[0], 0);
2972c393a42Smrg	default:
298a6844aabSmrg	    usage (argv[0], 1);
2992c393a42Smrg	}
3002c393a42Smrg    }
3012c393a42Smrg    i = optind;
3022c393a42Smrg#else
3032c393a42Smrg    i = 1;
3042c393a42Smrg#endif
3052c393a42Smrg
3062c393a42Smrg    config = FcInitLoadConfig ();
3072c393a42Smrg    if (!config)
3082c393a42Smrg    {
309a32e9e42Smrg	fprintf (stderr, _("%s: Can't initialize font config library\n"), argv[0]);
3102c393a42Smrg	return 1;
3112c393a42Smrg    }
3122c393a42Smrg    FcConfigSetCurrent (config);
313953daebaSmrg    FcConfigDestroy (config);
3142c393a42Smrg
3152c393a42Smrg    args = FcStrSetCreate ();
3162c393a42Smrg    if (!args)
3172c393a42Smrg    {
318a32e9e42Smrg	fprintf (stderr, _("%s: malloc failure\n"), argv[0]);
3192c393a42Smrg	return 1;
3202c393a42Smrg    }
3212c393a42Smrg    if (i < argc)
3222c393a42Smrg    {
3232c393a42Smrg	for (; i < argc; i++)
3242c393a42Smrg	{
3252c393a42Smrg	    if (!FcStrSetAddFilename (args, (const FcChar8 *) argv[i]))
3262c393a42Smrg	    {
327a32e9e42Smrg		fprintf (stderr, _("%s: malloc failure\n"), argv[0]);
3282c393a42Smrg		return 1;
3292c393a42Smrg	    }
3302c393a42Smrg	}
3312c393a42Smrg    }
3322c393a42Smrg    else
3332c393a42Smrg    {
3342c393a42Smrg	recurse++;
3352c393a42Smrg	arglist = FcConfigGetFontDirs (config);
3362c393a42Smrg	while ((arg = FcStrListNext (arglist)))
3372c393a42Smrg	    if (!FcStrSetAdd (args, arg))
3382c393a42Smrg	    {
339a32e9e42Smrg		fprintf (stderr, _("%s: malloc failure\n"), argv[0]);
3402c393a42Smrg		return 1;
3412c393a42Smrg	    }
3422c393a42Smrg	FcStrListDone (arglist);
3432c393a42Smrg    }
3442c393a42Smrg    arglist = FcStrListCreate (args);
3452c393a42Smrg    if (!arglist)
3462c393a42Smrg    {
347a32e9e42Smrg	fprintf (stderr, _("%s: malloc failure\n"), argv[0]);
3482c393a42Smrg	return 1;
3492c393a42Smrg    }
350953daebaSmrg    FcStrSetDestroy (args);
3512c393a42Smrg
3522c393a42Smrg    while ((arg = FcStrListNext (arglist)))
3532c393a42Smrg    {
3542c393a42Smrg	int	    j;
3552c393a42Smrg	FcChar8	    *cache_file = NULL;
3562c393a42Smrg	struct stat file_stat;
357953daebaSmrg
358953daebaSmrg	/* reset errno */
359953daebaSmrg	errno = 0;
3602c393a42Smrg	if (FcFileIsDir (arg))
3612c393a42Smrg	    cache = FcDirCacheLoad (arg, config, &cache_file);
3622c393a42Smrg	else
3632c393a42Smrg	    cache = FcDirCacheLoadFile (arg, &file_stat);
3642c393a42Smrg	if (!cache)
3652c393a42Smrg	{
366953daebaSmrg	    if (errno != 0)
367953daebaSmrg		perror ((char *) arg);
368953daebaSmrg	    else
369953daebaSmrg		fprintf (stderr, "%s: Unable to load the cache: %s\n", argv[0], arg);
3702c393a42Smrg	    ret++;
3712c393a42Smrg	    continue;
3722c393a42Smrg	}
3732c393a42Smrg
3742c393a42Smrg	dirs = FcStrSetCreate ();
3752c393a42Smrg	fs = FcCacheCopySet (cache);
3762c393a42Smrg	for (j = 0; j < FcCacheNumSubdir (cache); j++)
3772c393a42Smrg	{
3782c393a42Smrg	    FcStrSetAdd (dirs, FcCacheSubdir (cache, j));
3792c393a42Smrg	    if (recurse)
3802c393a42Smrg		FcStrSetAdd (args, FcCacheSubdir (cache, j));
3812c393a42Smrg	}
3822c393a42Smrg
3832c393a42Smrg	if (verbose)
3842c393a42Smrg	{
3852c393a42Smrg	    if (!first)
3862c393a42Smrg		printf ("\n");
387a32e9e42Smrg	    printf (_("Directory: %s\nCache: %s\n--------\n"),
3882c393a42Smrg		    FcCacheDir(cache), cache_file ? cache_file : arg);
3892c393a42Smrg	    first = FcFalse;
3902c393a42Smrg	}
3912c393a42Smrg        cache_print_set (fs, dirs, FcCacheDir (cache), verbose);
3922c393a42Smrg
3932c393a42Smrg	FcStrSetDestroy (dirs);
3942c393a42Smrg
3952c393a42Smrg	FcFontSetDestroy (fs);
3962c393a42Smrg	FcDirCacheUnload (cache);
3972c393a42Smrg	if (cache_file)
3982c393a42Smrg	    FcStrFree (cache_file);
3992c393a42Smrg    }
400953daebaSmrg    FcStrListDone (arglist);
4012c393a42Smrg
402ca08ab68Smrg    FcFini ();
4032c393a42Smrg    return 0;
4042c393a42Smrg}
405