fc-cat.c revision a32e9e42
1/*
2 * fontconfig/fc-cat/fc-cat.c
3 *
4 * Copyright © 2002 Keith Packard
5 *
6 * Permission to use, copy, modify, distribute, and sell this software and its
7 * documentation for any purpose is hereby granted without fee, provided that
8 * the above copyright notice appear in all copies and that both that
9 * copyright notice and this permission notice appear in supporting
10 * documentation, and that the name of the author(s) not be used in
11 * advertising or publicity pertaining to distribution of the software without
12 * specific, written prior permission.  The authors make no
13 * representations about the suitability of this software for any purpose.  It
14 * is provided "as is" without express or implied warranty.
15 *
16 * THE AUTHOR(S) DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
17 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
18 * EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY SPECIAL, INDIRECT OR
19 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
20 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
21 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
22 * PERFORMANCE OF THIS SOFTWARE.
23 */
24
25#ifdef HAVE_CONFIG_H
26#include <config.h>
27#else
28#ifdef linux
29#define HAVE_GETOPT_LONG 1
30#endif
31#define HAVE_GETOPT 1
32#endif
33
34#include <fontconfig/fontconfig.h>
35#include "../src/fcarch.h"
36#include <stdio.h>
37#include <stdlib.h>
38#include <string.h>
39#include <unistd.h>
40#include <sys/types.h>
41#include <sys/stat.h>
42#include <errno.h>
43#include <locale.h>
44
45#ifdef ENABLE_NLS
46#include <libintl.h>
47#define _(x)		(dgettext(GETTEXT_PACKAGE, x))
48#else
49#define dgettext(d, s)	(s)
50#define _(x)		(x)
51#endif
52
53#ifndef HAVE_GETOPT
54#define HAVE_GETOPT 0
55#endif
56#ifndef HAVE_GETOPT_LONG
57#define HAVE_GETOPT_LONG 0
58#endif
59
60#if HAVE_GETOPT_LONG
61#undef  _GNU_SOURCE
62#define _GNU_SOURCE
63#include <getopt.h>
64const struct option longopts[] = {
65    {"version", 0, 0, 'V'},
66    {"verbose", 0, 0, 'v'},
67    {"recurse", 0, 0, 'r'},
68    {"help", 0, 0, 'h'},
69    {NULL,0,0,0},
70};
71#else
72#if HAVE_GETOPT
73extern char *optarg;
74extern int optind, opterr, optopt;
75#endif
76#endif
77
78/*
79 * POSIX has broken stdio so that putc must do thread-safe locking,
80 * this is a serious performance problem for applications doing large
81 * amounts of IO with putc (as is done here).  If available, use
82 * the putc_unlocked varient instead.
83 */
84
85#if defined(putc_unlocked) || defined(_IO_putc_unlocked)
86#define PUTC(c,f) putc_unlocked(c,f)
87#else
88#define PUTC(c,f) putc(c,f)
89#endif
90
91static FcBool
92write_chars (FILE *f, const FcChar8 *chars)
93{
94    FcChar8    c;
95    while ((c = *chars++))
96    {
97	switch (c) {
98	case '"':
99	case '\\':
100	    if (PUTC ('\\', f) == EOF)
101		return FcFalse;
102	    /* fall through */
103	default:
104	    if (PUTC (c, f) == EOF)
105		return FcFalse;
106	}
107    }
108    return FcTrue;
109}
110
111static FcBool
112write_ulong (FILE *f, unsigned long t)
113{
114    int	    pow;
115    unsigned long   temp, digit;
116
117    temp = t;
118    pow = 1;
119    while (temp >= 10)
120    {
121	temp /= 10;
122	pow *= 10;
123    }
124    temp = t;
125    while (pow)
126    {
127	digit = temp / pow;
128	if (PUTC ((char) digit + '0', f) == EOF)
129	    return FcFalse;
130	temp = temp - pow * digit;
131	pow = pow / 10;
132    }
133    return FcTrue;
134}
135
136static FcBool
137write_int (FILE *f, int i)
138{
139    return write_ulong (f, (unsigned long) i);
140}
141
142static FcBool
143write_string (FILE *f, const FcChar8 *string)
144{
145
146    if (PUTC ('"', f) == EOF)
147	return FcFalse;
148    if (!write_chars (f, string))
149	return FcFalse;
150    if (PUTC ('"', f) == EOF)
151	return FcFalse;
152    return FcTrue;
153}
154
155static void
156usage (char *program, int error)
157{
158    FILE *file = error ? stderr : stdout;
159#if HAVE_GETOPT_LONG
160    fprintf (file, _("usage: %s [-rv] [--recurse] [--verbose] [*-%s" FC_CACHE_SUFFIX "|directory]...\n"),
161	     program, FC_ARCHITECTURE);
162    fprintf (file, "       %s [-Vh] [--version] [--help]\n", program);
163#else
164    fprintf (file, _("usage: %s [-rvVh] [*-%s" FC_CACHE_SUFFIX "|directory]...\n"),
165	     program, FC_ARCHITECTURE);
166#endif
167    fprintf (file, _("Reads font information cache from:\n"));
168    fprintf (file, _(" 1) specified fontconfig cache file\n"));
169    fprintf (file, _(" 2) related to a particular font directory\n"));
170    fprintf (file, "\n");
171#if HAVE_GETOPT_LONG
172    fprintf (file, _("  -r, --recurse        recurse into subdirectories\n"));
173    fprintf (file, _("  -v, --verbose        be verbose\n"));
174    fprintf (file, _("  -V, --version        display font config version and exit\n"));
175    fprintf (file, _("  -h, --help           display this help and exit\n"));
176#else
177    fprintf (file, _("  -r         (recurse) recurse into subdirectories\n"));
178    fprintf (file, _("  -v         (verbose) be verbose\n"));
179    fprintf (file, _("  -V         (version) display font config version and exit\n"));
180    fprintf (file, _("  -h         (help)    display this help and exit\n"));
181#endif
182    exit (error);
183}
184
185/*
186 * return the path from the directory containing 'cache' to 'file'
187 */
188
189static const FcChar8 *
190file_base_name (const FcChar8 *cache, const FcChar8 *file)
191{
192    int		    cache_len = strlen ((char *) cache);
193
194    if (!strncmp ((char *) cache, (char *) file, cache_len) && file[cache_len] == '/')
195	return file + cache_len + 1;
196    return file;
197}
198
199#define FC_FONT_FILE_DIR	((FcChar8 *) ".dir")
200
201static FcBool
202cache_print_set (FcFontSet *set, FcStrSet *dirs, const FcChar8 *base_name, FcBool verbose)
203{
204    FcChar8	    *dir;
205    const FcChar8   *base;
206    int		    n;
207    int		    ndir = 0;
208    FcStrList	    *list;
209
210    list = FcStrListCreate (dirs);
211    if (!list)
212	goto bail2;
213
214    while ((dir = FcStrListNext (list)))
215    {
216	base = file_base_name (base_name, dir);
217	if (!write_string (stdout, base))
218	    goto bail3;
219	if (PUTC (' ', stdout) == EOF)
220	    goto bail3;
221	if (!write_int (stdout, 0))
222	    goto bail3;
223        if (PUTC (' ', stdout) == EOF)
224	    goto bail3;
225	if (!write_string (stdout, FC_FONT_FILE_DIR))
226	    goto bail3;
227	if (PUTC ('\n', stdout) == EOF)
228	    goto bail3;
229	ndir++;
230    }
231
232    for (n = 0; n < set->nfont; n++)
233    {
234	FcPattern   *font = set->fonts[n];
235	FcChar8 *s;
236
237	s = FcPatternFormat (font, (const FcChar8 *) "%{=fccat}\n");
238	if (s)
239	{
240	    printf ("%s", s);
241	    FcStrFree (s);
242	}
243    }
244    if (verbose && !set->nfont && !ndir)
245	printf ("<empty>\n");
246
247    FcStrListDone (list);
248
249    return FcTrue;
250
251bail3:
252    FcStrListDone (list);
253bail2:
254    return FcFalse;
255}
256
257int
258main (int argc, char **argv)
259{
260    int		i;
261    int		ret = 0;
262    FcFontSet	*fs;
263    FcStrSet    *dirs;
264    FcStrSet	*args = NULL;
265    FcStrList	*arglist;
266    FcCache	*cache;
267    FcConfig	*config;
268    FcChar8	*arg;
269    int		verbose = 0;
270    int		recurse = 0;
271    FcBool	first = FcTrue;
272#if HAVE_GETOPT_LONG || HAVE_GETOPT
273    int		c;
274
275    setlocale (LC_ALL, "");
276#if HAVE_GETOPT_LONG
277    while ((c = getopt_long (argc, argv, "Vvrh", longopts, NULL)) != -1)
278#else
279    while ((c = getopt (argc, argv, "Vvrh")) != -1)
280#endif
281    {
282	switch (c) {
283	case 'V':
284	    fprintf (stderr, "fontconfig version %d.%d.%d\n",
285		     FC_MAJOR, FC_MINOR, FC_REVISION);
286	    exit (0);
287	case 'v':
288	    verbose++;
289	    break;
290	case 'r':
291	    recurse++;
292	    break;
293	case 'h':
294	    usage (argv[0], 0);
295	default:
296	    usage (argv[0], 1);
297	}
298    }
299    i = optind;
300#else
301    i = 1;
302#endif
303
304    config = FcInitLoadConfig ();
305    if (!config)
306    {
307	fprintf (stderr, _("%s: Can't initialize font config library\n"), argv[0]);
308	return 1;
309    }
310    FcConfigSetCurrent (config);
311    FcConfigDestroy (config);
312
313    args = FcStrSetCreate ();
314    if (!args)
315    {
316	fprintf (stderr, _("%s: malloc failure\n"), argv[0]);
317	return 1;
318    }
319    if (i < argc)
320    {
321	for (; i < argc; i++)
322	{
323	    if (!FcStrSetAddFilename (args, (const FcChar8 *) argv[i]))
324	    {
325		fprintf (stderr, _("%s: malloc failure\n"), argv[0]);
326		return 1;
327	    }
328	}
329    }
330    else
331    {
332	recurse++;
333	arglist = FcConfigGetFontDirs (config);
334	while ((arg = FcStrListNext (arglist)))
335	    if (!FcStrSetAdd (args, arg))
336	    {
337		fprintf (stderr, _("%s: malloc failure\n"), argv[0]);
338		return 1;
339	    }
340	FcStrListDone (arglist);
341    }
342    arglist = FcStrListCreate (args);
343    if (!arglist)
344    {
345	fprintf (stderr, _("%s: malloc failure\n"), argv[0]);
346	return 1;
347    }
348    FcStrSetDestroy (args);
349
350    while ((arg = FcStrListNext (arglist)))
351    {
352	int	    j;
353	FcChar8	    *cache_file = NULL;
354	struct stat file_stat;
355
356	/* reset errno */
357	errno = 0;
358	if (FcFileIsDir (arg))
359	    cache = FcDirCacheLoad (arg, config, &cache_file);
360	else
361	    cache = FcDirCacheLoadFile (arg, &file_stat);
362	if (!cache)
363	{
364	    if (errno != 0)
365		perror ((char *) arg);
366	    else
367		fprintf (stderr, "%s: Unable to load the cache: %s\n", argv[0], arg);
368	    ret++;
369	    continue;
370	}
371
372	dirs = FcStrSetCreate ();
373	fs = FcCacheCopySet (cache);
374	for (j = 0; j < FcCacheNumSubdir (cache); j++)
375	{
376	    FcStrSetAdd (dirs, FcCacheSubdir (cache, j));
377	    if (recurse)
378		FcStrSetAdd (args, FcCacheSubdir (cache, j));
379	}
380
381	if (verbose)
382	{
383	    if (!first)
384		printf ("\n");
385	    printf (_("Directory: %s\nCache: %s\n--------\n"),
386		    FcCacheDir(cache), cache_file ? cache_file : arg);
387	    first = FcFalse;
388	}
389        cache_print_set (fs, dirs, FcCacheDir (cache), verbose);
390
391	FcStrSetDestroy (dirs);
392
393	FcFontSetDestroy (fs);
394	FcDirCacheUnload (cache);
395	if (cache_file)
396	    FcStrFree (cache_file);
397    }
398    FcStrListDone (arglist);
399
400    FcFini ();
401    return 0;
402}
403