1/*
2 * fontconfig/test/test-bz89617.c
3 *
4 * Copyright © 2000 Keith Packard
5 * Copyright © 2018 Akira TAGOH
6 *
7 * Permission to use, copy, modify, distribute, and sell this software and its
8 * documentation for any purpose is hereby granted without fee, provided that
9 * the above copyright notice appear in all copies and that both that
10 * copyright notice and this permission notice appear in supporting
11 * documentation, and that the name of the author(s) not be used in
12 * advertising or publicity pertaining to distribution of the software without
13 * specific, written prior permission.  The authors make no
14 * representations about the suitability of this software for any purpose.  It
15 * is provided "as is" without express or implied warranty.
16 *
17 * THE AUTHOR(S) DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
18 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
19 * EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY SPECIAL, INDIRECT OR
20 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
21 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
22 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
23 * PERFORMANCE OF THIS SOFTWARE.
24 */
25#ifdef HAVE_CONFIG_H
26#include "config.h"
27#endif
28#include <stdio.h>
29#include <stdlib.h>
30#include <string.h>
31#include <dirent.h>
32#include <errno.h>
33#ifdef HAVE_UNISTD_H
34#include <unistd.h>
35#endif
36#ifndef HAVE_STRUCT_DIRENT_D_TYPE
37#include <sys/types.h>
38#include <sys/stat.h>
39#endif
40#include <fontconfig/fontconfig.h>
41
42#ifdef _WIN32
43#  define FC_DIR_SEPARATOR         '\\'
44#  define FC_DIR_SEPARATOR_S       "\\"
45#else
46#  define FC_DIR_SEPARATOR         '/'
47#  define FC_DIR_SEPARATOR_S       "/"
48#endif
49
50#ifdef _WIN32
51#include <direct.h>
52#define mkdir(path,mode) _mkdir(path)
53#endif
54
55#ifdef HAVE_MKDTEMP
56#define fc_mkdtemp	mkdtemp
57#else
58char *
59fc_mkdtemp (char *template)
60{
61    if (!mktemp (template) || mkdir (template, 0700))
62	return NULL;
63
64    return template;
65}
66#endif
67
68FcBool
69mkdir_p (const char *dir)
70{
71    char *parent;
72    FcBool ret;
73
74    if (strlen (dir) == 0)
75	return FcFalse;
76    parent = (char *) FcStrDirname ((const FcChar8 *) dir);
77    if (!parent)
78	return FcFalse;
79    if (access (parent, F_OK) == 0)
80	ret = mkdir (dir, 0755) == 0 && chmod (dir, 0755) == 0;
81    else if (access (parent, F_OK) == -1)
82	ret = mkdir_p (parent) && (mkdir (dir, 0755) == 0) && chmod (dir, 0755) == 0;
83    else
84	ret = FcFalse;
85    free (parent);
86
87    return ret;
88}
89
90FcBool
91unlink_dirs (const char *dir)
92{
93    DIR *d = opendir (dir);
94    struct dirent *e;
95    size_t len = strlen (dir);
96    char *n = NULL;
97    FcBool ret = FcTrue;
98#ifndef HAVE_STRUCT_DIRENT_D_TYPE
99    struct stat statb;
100#endif
101
102    if (!d)
103	return FcFalse;
104    while ((e = readdir (d)) != NULL)
105    {
106	size_t l;
107
108	if (strcmp (e->d_name, ".") == 0 ||
109	    strcmp (e->d_name, "..") == 0)
110	    continue;
111	l = strlen (e->d_name) + 1;
112	if (n)
113	    free (n);
114	n = malloc (l + len + 1);
115	if (!n)
116	{
117	    ret = FcFalse;
118	    break;
119	}
120	strcpy (n, dir);
121	n[len] = FC_DIR_SEPARATOR;
122	strcpy (&n[len + 1], e->d_name);
123#ifdef HAVE_STRUCT_DIRENT_D_TYPE
124	if (e->d_type == DT_DIR)
125#else
126	if (stat (n, &statb) == -1)
127	{
128	    fprintf (stderr, "E: %s\n", n);
129	    ret = FcFalse;
130	    break;
131	}
132	if (S_ISDIR (statb.st_mode))
133#endif
134	{
135	    if (!unlink_dirs (n))
136	    {
137		fprintf (stderr, "E: %s\n", n);
138		ret = FcFalse;
139		break;
140	    }
141	}
142	else
143	{
144	    if (unlink (n) == -1)
145	    {
146		fprintf (stderr, "E: %s\n", n);
147		ret = FcFalse;
148		break;
149	    }
150	}
151    }
152    if (n)
153	free (n);
154    closedir (d);
155
156    if (rmdir (dir) == -1)
157    {
158	fprintf (stderr, "E: %s\n", dir);
159	return FcFalse;
160    }
161
162    return ret;
163}
164
165int
166main (void)
167{
168    FcChar8 *fontdir = NULL, *cachedir = NULL;
169    char *basedir, template[512] = "/tmp/bz106632-XXXXXX";
170    char cmd[512];
171    FcConfig *config;
172    const FcChar8 *tconf = (const FcChar8 *) "<fontconfig>\n"
173	"  <dir>%s</dir>\n"
174	"  <cachedir>%s</cachedir>\n"
175	"</fontconfig>\n";
176    char conf[1024];
177    int ret = 0;
178    FcFontSet *fs;
179    FcPattern *pat;
180
181    fprintf (stderr, "D: Creating tmp dir\n");
182    basedir = fc_mkdtemp (template);
183    if (!basedir)
184    {
185	fprintf (stderr, "%s: %s\n", template, strerror (errno));
186	goto bail;
187    }
188    fontdir = FcStrBuildFilename ((const FcChar8 *) basedir, (const FcChar8 *) "fonts", NULL);
189    cachedir = FcStrBuildFilename ((const FcChar8 *) basedir, (const FcChar8 *) "cache", NULL);
190    fprintf (stderr, "D: Creating %s\n", fontdir);
191    mkdir_p ((const char *) fontdir);
192    fprintf (stderr, "D: Creating %s\n", cachedir);
193    mkdir_p ((const char *) cachedir);
194
195    fprintf (stderr, "D: Copying %s to %s\n", FONTFILE, fontdir);
196    snprintf (cmd, 512, "sleep 1; cp -a %s %s; sleep 1", FONTFILE, fontdir);
197    (void) system (cmd);
198
199    fprintf (stderr, "D: Loading a config\n");
200    snprintf (conf, 1024, (const char *) tconf, fontdir, cachedir);
201    config = FcConfigCreate ();
202    if (!FcConfigParseAndLoadFromMemory (config, (const FcChar8 *) conf, FcTrue))
203    {
204	printf ("E: Unable to load config\n");
205	ret = 1;
206	goto bail;
207    }
208    if (!FcConfigBuildFonts (config))
209    {
210	printf ("E: unable to build fonts\n");
211	ret = 1;
212	goto bail;
213    }
214    fprintf (stderr, "D: Obtaining fonts information\n");
215    pat = FcPatternCreate ();
216    fs = FcFontList (config, pat, NULL);
217    FcPatternDestroy (pat);
218    if (!fs || fs->nfont != 1)
219    {
220	printf ("E: Unexpected the number of fonts: %d\n", !fs ? -1 : fs->nfont);
221	ret = 1;
222	goto bail;
223    }
224    FcFontSetDestroy (fs);
225    fprintf (stderr, "D: Removing %s\n", fontdir);
226    snprintf (cmd, 512, "sleep 1; rm -f %s%s*; sleep 1", fontdir, FC_DIR_SEPARATOR_S);
227    (void) system (cmd);
228    fprintf (stderr, "D: Reinitializing\n");
229    if (FcConfigUptoDate(config))
230    {
231	fprintf (stderr, "E: Config reports up-to-date\n");
232	ret = 2;
233	goto bail;
234    }
235    if (!FcInitReinitialize ())
236    {
237	fprintf (stderr, "E: Unable to reinitialize\n");
238	ret = 3;
239	goto bail;
240    }
241    if (FcConfigGetCurrent () == config)
242    {
243	fprintf (stderr, "E: config wasn't reloaded\n");
244	ret = 3;
245	goto bail;
246    }
247    FcConfigDestroy (config);
248
249    config = FcConfigCreate ();
250    if (!FcConfigParseAndLoadFromMemory (config, (const FcChar8 *) conf, FcTrue))
251    {
252	printf ("E: Unable to load config again\n");
253	ret = 4;
254	goto bail;
255    }
256    if (!FcConfigBuildFonts (config))
257    {
258	printf ("E: unable to build fonts again\n");
259	ret = 5;
260	goto bail;
261    }
262    fprintf (stderr, "D: Obtaining fonts information again\n");
263    pat = FcPatternCreate ();
264    fs = FcFontList (config, pat, NULL);
265    FcPatternDestroy (pat);
266    if (!fs || fs->nfont != 0)
267    {
268	printf ("E: Unexpected the number of fonts: %d\n", !fs ? -1 : fs->nfont);
269	ret = 1;
270	goto bail;
271    }
272    FcFontSetDestroy (fs);
273    fprintf (stderr, "D: Copying %s to %s\n", FONTFILE, fontdir);
274    snprintf (cmd, 512, "sleep 1; cp -a %s %s; sleep 1", FONTFILE, fontdir);
275    (void) system (cmd);
276    fprintf (stderr, "D: Reinitializing\n");
277    if (FcConfigUptoDate(config))
278    {
279	fprintf (stderr, "E: Config up-to-date after addition\n");
280	ret = 3;
281	goto bail;
282    }
283    if (!FcInitReinitialize ())
284    {
285	fprintf (stderr, "E: Unable to reinitialize\n");
286	ret = 2;
287	goto bail;
288    }
289    if (FcConfigGetCurrent () == config)
290    {
291	fprintf (stderr, "E: config wasn't reloaded\n");
292	ret = 3;
293	goto bail;
294    }
295    FcConfigDestroy (config);
296
297    config = FcConfigCreate ();
298    if (!FcConfigParseAndLoadFromMemory (config, (const FcChar8 *) conf, FcTrue))
299    {
300	printf ("E: Unable to load config again\n");
301	ret = 4;
302	goto bail;
303    }
304    if (!FcConfigBuildFonts (config))
305    {
306	printf ("E: unable to build fonts again\n");
307	ret = 5;
308	goto bail;
309    }
310    fprintf (stderr, "D: Obtaining fonts information\n");
311    pat = FcPatternCreate ();
312    fs = FcFontList (config, pat, NULL);
313    FcPatternDestroy (pat);
314    if (!fs || fs->nfont != 1)
315    {
316	printf ("E: Unexpected the number of fonts: %d\n", !fs ? -1 : fs->nfont);
317	ret = 1;
318	goto bail;
319    }
320    FcFontSetDestroy (fs);
321    FcConfigDestroy (config);
322
323bail:
324    fprintf (stderr, "Cleaning up\n");
325    if (basedir)
326	unlink_dirs (basedir);
327    if (fontdir)
328	FcStrFree (fontdir);
329    if (cachedir)
330	FcStrFree (cachedir);
331
332    return ret;
333}
334